题目描述

  1. 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
  2. 示例:
  3. 输入: nums = [1,3,-1,-3,5,3,6,7], k = 3
  4. 输出: [3,3,5,5,6,7]
  5. 解释:
  6. 滑动窗口的位置 最大值
  7. --------------- -----
  8. [1 3 -1] -3 5 3 6 7 3
  9. 1 [3 -1 -3] 5 3 6 7 3
  10. 1 3 [-1 -3 5] 3 6 7 5
  11. 1 3 -1 [-3 5 3] 6 7 5
  12. 1 3 -1 -3 [5 3 6] 7 6
  13. 1 3 -1 -3 5 [3 6 7] 7
  14. 提示:
  15. 你可以假设 k 总是有效的,在输入数组不为空的情况下,1 k 输入数组的大小。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:

这一题暴力模拟肯定都能写出,但肯定比的是优化

思路一:用一个大顶堆来维护这个滑动窗口?
每次需要从堆中去除第一个数,再将新的数加入这个堆
时间复杂度就是N入堆+N出堆

思路二:
仔细观察滑动的这个过程,其实有点像深度学习中的一个maxpooling的过程,保留一个区域内的max值
所以,其实我们可以记录,某一个数比较大,出现在滑动窗口中,那么之前比他小的数字都其实可以不用去操作了,直接删除,不用去做比较
单调队列的思想
我用一个单调队列来保存滑动窗口中出现的最大值 次大值等信息
这个信息因为和坐标有关,所以保存的时候需要记录坐标,注意,一个数字的左边小于它的数字,肯定是不用管的(需要删除)

可以简化一下,就用一个单独的队列来保存位置
至于具体的数值可以用索引来访问?

image.png
所以这一题我一开始想着用queue来做发现中间删除的操作很难完成
后来发现其实双端队列来实现确实不错

代码:

  1. class Solution {
  2. public:
  3. vector<int> maxSlidingWindow(vector<int>& nums, int k) {
  4. int n = nums.size();
  5. deque<int> q;
  6. vector<int> ans;
  7. if(n==0) return ans;
  8. for (int i = 0; i < k; ++i) {
  9. while (!q.empty() && nums[i] >= nums[q.back()]) {
  10. q.pop_back();
  11. }
  12. q.push_back(i);
  13. }
  14. ans = {nums[q.front()]};
  15. for (int i = k; i < n; ++i) {
  16. while (!q.empty() && nums[i] >= nums[q.back()]) {
  17. q.pop_back();
  18. }
  19. q.push_back(i);
  20. while (q.front() <= i - k) {
  21. q.pop_front();
  22. }
  23. ans.push_back(nums[q.front()]);
  24. }
  25. return ans;
  26. }

复杂度分析:

滑动窗口的过程是O(n)级别的
然后每次滑动的过程中的操作数量的常数级别的
所以是O(n)
然后空间复杂度的话需要一个双端队列,O(k),一个ans数组O(n)