滑动窗口最大值

239. 滑动窗口最大值 - 力扣(LeetCode)

题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

示例

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       31 [3  -1  -3] 5  3  6  7       31  3 [-1  -3  5] 3  6  7       51  3  -1 [-3  5  3] 6  7       51  3  -1  -3 [5  3  6] 7       61  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

题解

分析

该题的解题思路其实很明确

  • 维护一个滑动窗口遍历数组
  • 每次计算滑动窗口中的最大值

但问题在于如何求滑动窗口中的最大值,因此问题就转换成了如何用最少的时间求一个序列的最大值

一般情况下我们有以下几种算法

  • 直接在序列中暴力遍历
  • 使用内置sort函数 

显然,外部使用滑动窗口遍历数组的时间已经达到了O(n),如果我们采取以上方式计算最大值,那么我们最终的算法时间复杂度必然已经达到了O(n^2)

再次观察,由于我们只需要获取序列的最大值,并不关心序列的顺序,我们要问:

有没有一种数据结构,可以每次将序列元素放进去之后,自动更新最大值,并将最大值放到头部呢?这样我们只需将滑动窗口的数据放到这个数据结构中,然再从这个数据结构的头部取出元素不就成功解决问题了么

而数据结构中恰好有一种数据结构——,可以完成这个任务

当我们将一个序列放进一个大顶堆时,堆会自动调整,将最大值放到堆顶,我们只需要获取堆顶的元素即可

因此,算法的思路就变成了

  • 维护一个滑动窗口,遍历数组中的元素
  • 每次将滑动窗口中的序列放进大顶堆中,大顶堆会自动进行调整,将序列的最大值放到堆顶

而我们知道

遍历数组的时间是O(n),而堆调整的时间是O(log n),因此整个算法的时间复杂度就变成了O(n*log n) 

这样我们就得到了以下两种解法

  • 维护一个大顶堆
  • 使用优先级队列

除此之外,我们完全可以自己维护一个单调队列,这个队列中的元素按照降序排序,队头的元素永远是序列的最大值,这样我们只需要每次从队头中取出元素即可,而从队头取出元素的时间为O(1),因此整个的时间复杂度是O(n)

接下来,我们就优先级队列和单调队列进行说明

优先级队列

首先我们需要知道的是

优先级队列的底层数据结构就是堆,因此我们使用优先级队列这种数据结构解题,本质上就是在使用堆

有关优先级队列的使用和介绍可参考

std::priority_queue - cppreference.com

算法思路其实和上述解释的过程是一样的

  • 维护滑动窗口,并将滑动窗口中的数值放到优先级队列中
  • 优先级队列底层默认使用的是大顶堆,因此会自动进行堆调整,将最大值放到堆顶
  • 故只需取出堆顶的元素即可 

但问题在于, 

  • 我们需要保证优先级队列中的数据永远是当前滑动窗口中的序列这样才能保证优先级队列中堆顶元素的最大值是当前滑动窗口的最大值
  • 因此我们需要在移动滑动窗口时,将上一个滑动窗口中的元素pop掉
  • 但优先级队列中只有堆顶元素是确定的,我们无法确定被pop的元素在队列中的位置

 但上述条件其实不需要完全满足,也就是说

  • 我们不必确定当前优先级队列中的所有元素都一定是滑动窗口中的元素
  • 我们只需要确保当前优先级队列中的队头元素一定是当前滑动窗口中的元素,而不是上一个滑动窗口中的元素即可

所以,

我们在push优先级队列元素的时候,需要将元素在nums中的下标位置也记录下来,用于确定优先级队列中堆顶的元素是否是当前滑动窗口中的元素

故整个代码如下

class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {//优先级队列,维护大顶堆std::priority_queue<std::pair<int,int>> pq;std::vector<int> res;//先处理第一个滑动窗口for(int i=0;i<k;++i)pq.emplace(nums[i],i);res.emplace_back(pq.top().first);//接着遍历剩余的滑动窗口for(int i=k;i<nums.size();++i){pq.emplace(nums[i],i);//只要发现堆顶中元素不在当前滑动窗口中,就将其剔除掉while(pq.top().second<(i-k+1)){pq.pop();}res.emplace_back(pq.top().first);}return res;}
};

单调队列

首先,我们需要维护一个队列,并保证

  • 每次push进去元素时,最大值永远在队头

 因此,我们可以这样写

        void push(const int& val){//只要push的元素val比队尾的元素大,就将其pop掉,直到push的元素val能够放到队头while(!queue_.empty() && val>queue_.back())queue_.pop_back();queue_.emplace_back(val);}

假如现在有三个元素【1,-1,3】需要依次被push,则整个push过程如下图所示

而队列pop的过程较为简单,如果发现是需要被pop的元素,直接pop掉即可,如下所示

        void pop(const int& val){if(!queue_.empty() && val==queue_.front())queue_.pop_front();}

 基于这样的数据结构,于是我们每次只需获取队头元素,即可获取到当前序列的最大值

        int getFront() const {return queue_.front();}

完整的单调队列代码如下所示

    class Queue{public:void push(const int& val){while(!queue_.empty() && val>queue_.back())queue_.pop_back();queue_.emplace_back(val);}void pop(const int& val){if(!queue_.empty() && val==queue_.front())queue_.pop_front();}int getFront() const {return queue_.front();}private:std::deque<int> queue_;};

于是整个解题过程就顺利成章的变为

    vector<int> maxSlidingWindow(vector<int>& nums, int k) {Queue queue;std::vector<int> res;//先处理第一个滑动窗口        for(int i=0;i<k;++i)queue.push(nums[i]);res.emplace_back(queue.getFront());//接下来移动滑动窗口,遍历数组,并将元素放入queue中for(int i=k;i<nums.size();++i){//将当前滑动窗口左侧边界的数值剔除掉queue,保证当前queue中维护是当前滑动窗口中的序列queue.pop(nums[i-k]);//将当前滑动窗口中的值push进queue中queue.push(nums[i]);//由我们维护的单调队列的性质,只需取出队头的元素,即是滑动窗口中的最大值res.emplace_back(queue.getFront());}return res;}

整个解题代码如下所示

class Solution {
public:class Queue{public:void push(const int& val){while(!queue_.empty() && val>queue_.back())queue_.pop_back();queue_.emplace_back(val);}void pop(const int& val){if(!queue_.empty() && val==queue_.front())queue_.pop_front();}int getFront() const {return queue_.front();}private:std::deque<int> queue_;};vector<int> maxSlidingWindow(vector<int>& nums, int k) {Queue queue;std::vector<int> res;for(int i=0;i<k;++i)queue.push(nums[i]);res.emplace_back(queue.getFront());for(int i=k;i<nums.size();++i){queue.pop(nums[i-k]);queue.push(nums[i]);res.emplace_back(queue.getFront());}return res;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/8855.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

GEE 案例——利用哨兵-2 图像时间序列和谷歌地球引擎云计算自动绘制和监测香港海洋水质参数

目录 简介 结论 代码 结果 APP链接 引用 简介 对沿海水质的持续监测对于水资源管理和海洋生态系统的可持续性至关重要。 遥感数据&#xff08;如哨兵-2 卫星图像&#xff09;可提供用于时间序列分析的高分辨率观测数据&#xff0c;而基于云的谷歌地球引擎&#xff08;GE…

Redis4:Redis的Java客户端

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

基于Java Web的传智播客crm企业管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

【Eclipse系列】eclipse安装与常规配置(含插件)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、下载与安装 二、常规设置 1.1.设置工作空间(workspace) 1.2.设置字体和字体大小 ​编辑 1.3.设置编码 1.4.去除验证(validation) 1.5.去除单词验证(spelli…

抗辐照MCU芯片工艺解析:如何保障芯片的可靠性

行星探索、轨道飞行器任务和空间研究在内的太空项目需要创新的航天器系统技术提供通信与处理功能。随着商业航天的发展&#xff0c;对于航天电子系统需要考虑高可靠与高性能的同时&#xff0c;还需要考虑降低开发成本和缩短上市时间。 以MCU芯片AS32A401为例&#xff0c;该芯片…

qt QKeySequence详解

1、概述 QKeySequence 是 Qt 框架中的一个类&#xff0c;用于表示和处理键盘快捷键序列。它提供了一种方便的方式来解析、存储和比较键盘快捷键&#xff0c;这些快捷键通常用于触发应用程序中的特定操作或命令。QKeySequence 支持多种格式的快捷键表示&#xff0c;包括单个按键…

【RMA】基于知识注入和模糊学习的多模态歧义分析

abstract 多模态情感分析&#xff08;MSA&#xff09;利用互补的多模态特征来预测情感极性&#xff0c;主要涉及语言、视觉和音频三种模态。现有的多模态融合方法主要考虑不同模态的互补性&#xff0c;而忽略了模态之间的冲突所导致的歧义&#xff08;即文本模态预测积极情绪&…

移动取证和 Android 安全

当今的数字时代已经产生了许多技术进步&#xff0c;无论是智能手机还是虚拟现实、人工智能和物联网 (IoT) 等下一代基础技术。 智能手机已不再只是奢侈品&#xff0c;而是我们生存所必需的东西。根据各种统计数据&#xff0c;如今全球有超过 50% 的人使用手机。 由于数据存储…

【Linux】简易版shell

文章目录 shell的基本框架PrintCommandLineGetCommandLineParseCommandLineExecuteCommandInitEnvCheckAndExecBuildCommand代码总览运行效果总结 shell的基本框架 要写一个命令行我们首先要写出基本框架。 打印命令行获取用户输入的命令分析命令执行命令 基本框架的代码&am…

Java 枚举

目录 枚举是什么 常用方法 构造方法 枚举的优缺点 枚举和反射 实现单例模式 枚举是什么 枚举&#xff08;enum&#xff09;&#xff1a;是一种特殊的类&#xff0c;用于定义一组常量&#xff0c;将其组织起来。枚举使得代码更具有可读性和可维护性&#xff0c;特别是在处…

【梯度下降法优化】随机梯度下降、牛顿法、动量法、Nesterov、AdaGrad、RMSprop、Adam

本文理论参考王木头的视频&#xff1a; “随机梯度下降、牛顿法、动量法、Nesterov、AdaGrad、RMSprop、Adam”&#xff0c;打包理解对梯度下降法的优化_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1r64y1s7fU/?spm_id_from333.999.0.0&vd_sourceecbdfcacb078d0…

五个高质量伤感视频素材资源站,帮你快速找到完美创作素材

在制作短视频、MV或者广告时&#xff0c;伤感主题的视频素材往往能触动观众的情感&#xff0c;让作品更具共鸣。无论是表达分手、离别&#xff0c;还是展现孤独与失落&#xff0c;合适的伤感素材对情感类创作至关重要。为帮助创作者找到优质的视频素材&#xff0c;以下推荐5个高…

天正建筑T20V8

链接: https://pan.baidu.com/s/1k-PcXJxHWPh3-6yAIfcaPg提取码: dvyn

JavaScript 实现文本转语音功能

全篇大概2000 字&#xff08;含代码&#xff09;&#xff0c;建议阅读时间10分钟。 引言 我将向大家展示如何使用 JavaScript 和 Web Speech API 快速实现一个“文本转语音”的 Web 应用。通过这个教程&#xff0c;你将了解如何让浏览器将输入的文本朗读出来。 预览效果 一、…

DNS域名详细解析详解

文章目录 DNS域名详细解析详解一、引言二、DNS域名解析过程1、DNS解析概述1.1、DNS解析的基本步骤 2、代码示例 三、DNS查询类型1、递归查询2、迭代查询 四、总结 DNS域名详细解析详解 一、引言 在互联网的世界里&#xff0c;域名和IP地址是两个不可或缺的概念。IP地址是计算…

函数计算——文档与网页数据提取工具(MinerU)应用实践

1 引言 在信息爆炸的时代&#xff0c;AI研究者面临着从海量文档中提取高质量数据的挑战。随着大语言模型在各个领域的广泛应用&#xff0c;有效地处理和整合文档信息成为了基础性的任务。这些文档形式多样&#xff0c;包括学术文献、行业报告、会议PPT、课本、说明书及合同单据…

【网络】应用层——HTTP协议

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是HTTP协议。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! > 专栏选自&#xff1a;网络 &g…

计算生物学与生物信息学漫谈-5-mapping算法

之前的文章我们介绍了参考基因组&#xff0c;也介绍了一些基本概念&#xff0c;具体可以看之前的博客&#xff1a; 计算生物学与生物信息学漫谈-4-参考基因组与Mapping准备_基因组的map-CSDN博客 这次我们讲如何将read map到基因组上所用到的各种算法&#xff1a; 目录 1.1 …

qsqlmysql.lib的编译和使用

文章目录 打开源码 打开源码 打开qt源码安装路径 src相对路径下的文件Src\qtbase\src\plugins\sqldrivers\mysql 比如我是5.9.9版本我的路径就是&#xff1a;D:\Qt5.9.9\5.9.9\Src\qtbase\src\plugins\sqldrivers\mysql 可以看到待编译的mysql驱动文件 使用IDE打开pro文件进…

leetcode 693.交替位二进制数

1.题目要求&#xff1a; 2.题目代码: class Solution { public:bool hasAlternatingBits(int n) {int num n;//设置数组存入二进制位vector<int> array;while(num){array.push_back(num % 2); num num / 2;}//把数组颠倒就能得到此数真正二进制位reverse(array.begin…