滑动窗口的最值处理以及延伸思考

前言:一直以来,对于一个给定长度为 n n n 的数组,都不知道应该如何求任意两个下标间的最小值,今天刚好碰到了这道题,也算是给我上了一课😂


我们简化问题:对于长度为 k k k 的窗口,求任意窗口间的最小值。之后只需要把窗口的范围变为 [ 2 , n ] [2, n] [2,n] 即可。

方法一:基于最大堆

class Solution {
public:
using PII = pair<int, int>;

    vector<int> minSlidingWindow(vector<int>& nums, int k) {
        priority_queue<PII, vector<PII>, greater<PII> > pq;

        int n = nums.size();
        for(int i = 0; i < k; ++i){
            pq.push({nums[i], i});
        }

        vector<int> ans;
        ans.push_back(pq.top().first);

        for(int i = k; i < n; ++i){
            pq.push({nums[i], i});
            while(pq.top().second <= i - k){
                pq.pop();
            }
            ans.push_back(pq.top().first);
        }
        return ans;
    }
};

对于这种 O ( n l o g n ) O(nlogn) O(nlogn) 复杂度的方法,要点就一个:对于堆内的元素,我们只关心 当前堆顶的元素 (即最小值) 所在下标是否超出了窗口的左边界。举个🌰让大家可以 结合代码 领悟一下:

  • 对于数组 [ 2, 3, 1, 4, 5, 6 ],演变过程如下
窗口变化堆内元素 ( { v a l , i n d e x } \{val, index\} {val,index})
[ 2 3 1 ] 4 5 6[ {1, 2}, {2, 0}, {3, 1} ]
2 [ 3 1 4 ] 5 6[ {1, 2}, {2, 0}, {3, 1}, {4, 3} ]
2 3 [ 1 4 5 ] 6[ {1, 2}, {2, 0}, {3, 1}, {4, 3}, {5, 4} ]
2 3 1 [ 4 5 6 ][ {3, 1}, {4, 3}, {5, 4} ]

对于表格的最后一行,此时 i − k = = 2 i-k == 2 ik==2,所以会连续 p o p ( ) pop() pop() 3 次, 这也是 while 循环存在的意义


方法二:单调队列

class Solution {
public:
    vector<int> minSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> ans;
        deque<int> q;
        for(int i = 0; i < n; ++i){
            while(q.size() && nums[q.back()] > nums[i]){
                q.pop_back();
            }
            q.push_back(i);
            if(i >= k-1){   // 窗口即将开始滑动
                ans.push_back(nums[q.front()]);
                if(i-k+1 == q.front()) 
                    q.pop_front();
            }
        }
        return ans;
    }
};
  • 单调队列的做法对于堆而言减少了数据结构内部的调整,所以时间复杂度仅为 O ( n ) O(n) O(n)

对于以上的代码,可真的是:妙蛙种子吃着妙脆角到了米奇妙妙屋,妙到家了!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值