原题链接Sliding Window Maximum

给定一个数组,从左到右每k个位置算作一个滑动窗,每到达一个滑动窗,都需要找到这个滑动窗中最大的元素并记录下来,最后返回所有最大元素组成的数组。要求时间复杂度在O(n)

首先,如果每到达一个滑动窗都计算一遍这个滑动窗覆盖的区域的最大值,那么复杂度应该是….额…O(n*k - k*k)?k是滑动窗大小,n是数组元素个数


有没有什么方法可以不用每次都计算一遍最大值呢

对于滑动窗而言,首先想到的就是向右移动的过程中是进行”左边减,右边加”的操作,即从滑动窗中删除离开窗口的元素,并增加进去窗口的元素

利用这个特性,假设已经知道第一个滑动窗nums[0 : k - 1]中的最大值N,那么在向右移动时,删掉nums[0],增加nums[k],然后将N和nums[k]的较大者作为第二个滑动窗的最大值

一切看起来非常完美,但是一旦N是nums[0],上面的想法突然变得,唔…,不太完善

不过现在任务倒是明朗不少,既然最大值N从窗口中离开,那么就需要知道在第一个滑动窗中第二大元素的值,拿它和nums[k]的较大者作为第二个滑动窗的最大值

问题是该怎么记,同时还要保证复杂度尽量低


假设…假设,有这么一个容器D,它记录的是某些元素的下标,同时这个容器保证在这些下标上的元素的大小顺序是递减的,也就是nums[D[0]] > nums[D[1]] > nums[D[2]] ….

另外,对于这个容器,规定

  • 可以获得第0的元素,即有成员函数D.front()
  • 可以获得最后一个元素,即有成员函数D.back()
  • 可以弹出第一个元素,即有成员函数D.pop_front()
  • 可以弹出最后一个元素,即有成员函数D.pop_back()
  • 可以插入一个元素到末尾,即有成员函数D.push_back()

而对于这个容易的操作,规定

  • 插入的是元素下标而非元素的值
  • 插入元素下标到末尾时,从容器末尾开始依次弹出指向的元素值小于要插入下标代表的元素值的那些下标

最后一个规定多少有些绕口,用数字表示可能会清楚些,假设某一时刻容器D中的元素为

I1, I2, I3, I4, I5

注意根据上面的规定,可以知道nums[I1] > nums[I2] > nums[I3] > nums[I4] > nums[I5]

此时遍历到一个元素M,它的下标为Im,那么如果要将Im插入D中时,需要从D的末尾开始,找到第一个下标Ii满足nums[Ii] > M,至于Ii+1, …, I5就都pop掉,然后将Im追加到D的末尾。

假设I3是第一个满足上面要求的值,那么插入Im后,D中元素为I1, I2, I3, Im。

I4和I5在向前找nums[Ii] > M的过程中已经被pop掉了


先提结论吧,根据上述规则,D中的下标分别是当前滑动窗第一大的元素,第二大的元素,…第n大的元素的下标。nums[D.front()]就是当前滑动窗覆盖区域中的最大值

为什么是记录下标而不是记录元素的值,因为通过记录下标,可以判断上一个滑动窗的最大值是否已经被移出了

假设某一时刻的滑动窗位于nums[i-k+1, i-k+2, …, i-2, i-1, i],此时nums[i-k+1]是这个滑动窗的最大值,同时也知道D.front() == i-k+1。

那么当向右移动一步时,滑动窗变为nums[i-k+2, i-k+3, …, i-1, i, i+1]

要怎么才可以知道上一个滑动窗的最大值已经被移出了呢,通过(i+1) - D.front() == k啦:)

此时就可以把D.front()弹出,通过调用D.pop_front()。随后把滑动窗新加进来的元素nums[i+1]追加到D中,追加之后,D.front()是当前滑动窗的最大值所在的位置

那么有没有上面这样的容器呢,没有,唔…我是说C++标准库里没有,但是有个容器,它有所有容器D需要的成员函数,啊终于轮到deque出场了(一直以为它只是作为幕后工作者的身份用来实现stack, queue,没想到还有这样的用处)

deque,俗话叫双端队列,在队列的两端都可以进行插入删除操作,并且复杂度是O(1)。不过对于上面的插入操作规则,需要手动去实现

实现完叫什么,单调队列:)

代码如下

class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> res;deque<int> dq;for(int i = 0; i < nums.size(); ++i){/* 上一个滑动窗的最大值被移出了,从容器中删掉 */if(!dq.empty() && i - dq.front() == k)dq.pop_front();/* 将容器末尾的所有指向的元素值小于当前值的下标都删掉 */while(!dq.empty() && nums[dq.back()] < nums[i])dq.pop_back();/* 将刚遍历到的元素下标放进容器中 */dq.push_back(i);/* dq.front()是当前滑动窗第一大的元素的下标 *//* i >= k-1时才到达第一个滑动窗 */if(i >= k - 1)res.push_back(nums[dq.front()]);}return res;}
};

本题主要利用deque实现单调队列,不容易理解的地方在为什么可以在插入一个下标时可以将其他指向值小于当前值的下标都pop掉

考虑上面的D容器,容器内部情况为I1, I2, I3, I4, I5

这5个值表示当前滑动窗的第一大的元素是nums[I1],第二大的元素是nums[I2],…, 第五大的元素是nums[I5]

同时,当前的滑动窗为nums[I1, I2, I3, …, I5],假设右移一次后滑动窗为nums[I2, I3, …, I5, I],根据D可知右移前滑动窗的最大值nums[I1]已经被移走,第二大的元素是nums[I2],此时D的情况为

I2, I3, I4, I5

假设nums[I] > nums[I2],根据上述规则,更新后的D为

I

没错只有I一个,因为其他的下标代表的值都小于nums[I],都被pop掉了。

为什么可以只有I一个呢,因为I的位置是当前滑动窗最右边的位置,只有右移k次后才会被移走,而在右移1, 2, …, k - 1时,都可以知道前k个滑动窗的最大值

每天一道LeetCode-----计算给定序列中所有长度为k的滑动窗的最大值集合相关推荐

  1. 每天一道LeetCode-----找到给定序列中所有和为某个值的集合或集合个数,序列中可以有/无重复项,集合元素顺序不同算不同集合等

    Combination Sum 原题链接Combination Sum 给定一个无重复项的序列,找到所有和是target的集合,每个元素可以使用多次. 可以用深度优先(dfs),对于某个元素都有两种选 ...

  2. Python计算分位点与逆运算:根据给定的值,计算在序列中的分位水平

    计算分位数 计算一个序列的分位点,使用: import numpy as npvalue_array = np.arange(0, 10, 1) print(np.quantile(value_arr ...

  3. Python---寻找给定序列中相差最小的两个数字

    编写函数,寻找给定序列中相差最小的两个数字 def getTwoClosestElements(arr):#先进行排序,使得相邻元素最接近#相差最小的元素必然相邻seq = sorted(arr)#先 ...

  4. 字符串的回文子序列个数_计算给定字符串中回文子序列的数量

    字符串的回文子序列个数 Problem statement: 问题陈述: Given a string you have to count the total number of palindromi ...

  5. pandas使用groupby函数计算dataframe数据中每个分组的N个数值的滚动最大值(rolling max)、例如,计算某公司的多个店铺每N天(5天)的滚动销售额最大值

    pandas使用groupby函数计算dataframe数据中每个分组的N个数值的滚动最大值(rolling max).例如,计算某公司的多个店铺每N天(5天)的滚动销售额最大值 目录

  6. 每天一道LeetCode-----寻找给定字符串中重复出现的子串

    Repeated DNA Sequences 原题链接Repeated DNA Sequences 在给定字符串中寻找重复出现的序列,每个序列长度为10 可以采用unordered_map记录每个序列 ...

  7. 每天一道LeetCode-----找出给定序列的所有子序列

    Subsets 原题链接Subsets 给定一个数组序列,找出所有子序列 深度优先扫一遍:) class Solution { public:vector<vector<int>&g ...

  8. 每天一道LeetCode-----找到给定数组中第三大的值

    原题链接Third Maximum Number 要求找到给定数组中第三大的数.其中第一大的数,第二大的数,第三大的数互不相同,即严格的小于关系.并且规定时间复杂度是O(n).另外如果找不到第三大的数 ...

  9. LeetCode 2099. 找到和最大的长度为 K 的子序列

    文章目录 1. 题目 2. 解题 1. 题目 给你一个整数数组 nums 和一个整数 k . 你需要找到 nums 中长度为 k 的 子序列 ,且这个子序列的 和最大 . 请你返回 任意 一个长度为 ...

最新文章

  1. js实现图片虚化_js canvas画布实现高斯模糊效果
  2. Dockerfile文件命令详解
  3. 重磅:ATT的《5G移动边缘计算白皮书》!
  4. sqlplus中调用shell_(转)shell 调用sqlplus各种情况示例
  5. 定义命令别名(alias)
  6. 重磅!谷歌大脑提出EfficientNet平衡模型扩展三个维度,取得精度-效率的最大化!...
  7. Java中HashMap和TreeMap的区别深入理解,java开发面试笔试题
  8. 操作系统原理常见面试题总结
  9. php中strrpos函数的返回值类型是型_PHP字符串处理的一些常用函数 - strrpos
  10. cad填充图案乱理石_CAD教程:CAD填充图案管理技巧
  11. c语言自动变量全局变量,C语言全局变量的一些简单介绍
  12. 如何获取论文的参考文献格式
  13. JAVA面向对象 从0.5到1
  14. C# 中的多线程和异步编程
  15. 表单验证之 formik 简单用法
  16. Binwalk的安装和使用
  17. 风华贴片电容命名规则
  18. Win10 基于Docker使用tensorflow serving部署模型
  19. 什么是Socket?Socket协议的形象描述
  20. NGUI用onSelect检测(监听)Input 控件失焦(非选中状态)

热门文章

  1. oracle jdbctype null,Oracle数据库之springboot 项目mybatis plus 设置 jdbcTypeForNull
  2. 浙商银行2011.11.26校园招聘会笔试题
  3. 第四章 分治策略 4.1 最大子数组问题 (暴力求解算法)
  4. SQL Server Reporting Services(简称SSRS)
  5. SendMail与Postfix的架构备忘2
  6. 按汉字首字母排序(sql语句)
  7. C# 系统应用之使用Pancel控件同一窗体切换页面
  8. RxSwift之UI控件UIPickerView扩展的使用
  9. iOS之性能优化·提高App的编译速度
  10. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1114:白细胞计数