剑指offer——滑动窗口的最大值C++

在这里插入图片描述
暴力=easy。deque=hard。
由于我们需要求出的是滑动窗口的最大值,如果当前的滑动窗口中有两个下标 ii 和 jj,其中 ii 在 jj 的左侧(i < ji<j),并且 ii 对应的元素不大于 jj 对应的元素(\textit{nums}[i] \leq \textit{nums}[j]nums[i]≤nums[j]),那么会发生什么呢?

当滑动窗口向右移动时,只要 ii 还在窗口中,那么 jj 一定也还在窗口中,这是 ii 在 jj 的左侧所保证的。因此,由于 \textit{nums}[j]nums[j] 的存在,\textit{nums}[i]nums[i] 一定不会是滑动窗口中的最大值了,我们可以将 \textit{nums}[i]nums[i] 永久地移除。

因此我们可以使用一个队列存储所有还没有被移除的下标。在队列中,这些下标按照从小到大的顺序被存储,并且它们在数组 \textit{nums}nums 中对应的值是严格单调递减的。因为如果队列中有两个相邻的下标,它们对应的值相等或者递增,那么令前者为 ii,后者为 jj,就对应了上面所说的情况,即 \textit{nums}[i]nums[i] 会被移除,这就产生了矛盾。

当滑动窗口向右移动时,我们需要把一个新的元素放入队列中。为了保持队列的性质,我们会不断地将新的元素与队尾的元素相比较,如果前者大于等于后者,那么队尾的元素就可以被永久地移除,我们将其弹出队列。我们需要不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。

由于队列中下标对应的元素是严格单调递减的,因此此时队首下标对应的元素就是滑动窗口中的最大值。但与方法一中相同的是,此时的最大值可能在滑动窗口左边界的左侧,并且随着窗口向右移动,它永远不可能出现在滑动窗口中了。因此我们还需要不断从队首弹出元素,直到队首元素在窗口中为止。

为了可以同时弹出队首和队尾的元素,我们需要使用双端队列。满足这种单调性的双端队列一般称作「单调队列」。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetco-ki6m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        //找每个滑动窗口的最大值,不是找最大的滑动窗口
        //单调队列,用双端队列实现,队列中存的是数字的下标,res存的是下标对应的值
        vector<int> res;
        if(num.size() < size||size<1) return res;
        deque<int> index;//存的是下标,用下标可以判断front()的数是否已经不在该窗口内
        for(int i = 0; i < size; ++i){
            //如果当前插入的值大于等于队尾,直接弹出队尾,因为新加进来的一定更新,留住它就行了
            while(!index.empty() && num[index.back()] <= num[i])
                index.pop_back();
            index.push_back(i);
        }
        for(int i = size; i < num.size(); ++i){
            res.push_back(num[index.front()]);
            
            while(!index.empty() && num[index.back()] <= num[i])
                index.pop_back();
            //也要判断是否为空,不然取队头会报错
            if(!index.empty() && i - index.front()>=size)
                index.pop_front();
            
            index.push_back(i);
        }
        //最后一次的窗口最大值还没被加进来
        res.push_back(num[index.front()]);
        return res;
    }
};

很棒的题,记得最后将那个没有加入res的滑动窗口最大值加入进去

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> dq;
        vector<int> res;
        //先创建一个大小为k的滑动窗口
        //用双端队列维护一个队首是最大元素的滑动窗口
        for(int i = 0; i < k; ++i){
            while(!dq.empty() && nums[dq.back()] <= nums[i]){
                dq.pop_back();
            }
            dq.push_back(i);
        }
        for(int i = k; i < nums.size(); ++i){
            //先将第一个滑动窗口的最大值加入数组
            res.push_back(nums[dq.front()]);
            while(!dq.empty() && nums[dq.back()] <= nums[i]){
                dq.pop_back();
            }
            dq.push_back(i);
            if(i-dq.front() >= k){
                dq.pop_front();
            }
        }
        //最后一个滑动窗口的值没有加入进来
        res.push_back(nums[dq.front()]);
        return res;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值