【剑指offer-C++】JZ59:滑动窗口的最大值

JZ59:滑动窗口的最大值

题目描述

描述:给定一个长度为 n 的数组 num 和滑动窗口的大小 size ,找出所有滑动窗口里数值的最大值。

例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

窗口大于数组长度或窗口长度为0的时候,返回空。

数据范围: 1≤n≤10000,0≤size≤10000,数组中每个元素的值满足 ∣val∣≤10000。

要求:空间复杂度 O(n),时间复杂度 O(n)。

输入:[2,3,4,2,6,2,5,1],3
返回值:[4,4,6,6,6,5]
输入:[9,10,9,-7,-3,8,2,-6],5
返回值:[10,10,9,8]
输入:[1,2,3,4],5
返回值:[]

解题思路

滑动窗口的最大值:最直观的想法是,两层for循环,外层i表示每次窗口的起始位置,内层j表示每次窗口的结束位置,遍历窗口内的每一个元素,然后取窗口内的最大值,并加入到结果数组中。注意,当数组为空时或者滑动窗口值大于数组长度,要返回空数组。(牛客这个暴力方法居然过啦)

vector<int> maxInWindows(const vector<int>& num, unsigned int size) 
{
     vector<int> res;
     // 数组为空 或者 滑动窗口值大于数组长度
     if(num.size()==0||size>num.size())
        return res;
     int maxn;
     // i表示起始位置
     for(int i=0;i+size-1<num.size();i++)
     {
         maxn=num[i];
         // j表示结束位置 遍历整个窗口
         for(int j=i+1;j<=i+size-1;j++)
         {
            maxn=max(num[j],maxn);
         }
         // 将最大值加入结果
         res.push_back(maxn);
     }
     return res;
}

优化:上述方法其实包含很多重复计算,我们可以通过分析来优化。如果当前元素大于前面元素,那么前面的元素不可能成为最大值,故需要将前面的元素从队尾出队;如果当前元素小于等于前面元素,那么需要将元素从队尾入队,因为该元素可能会成为后续的最大值。同时,我们要判断窗口内元素是否过期,即队头元素下标是否小于当前窗口左端,由于要方便统计窗口边界,故使用的是下标存储,当超过边界不合法时需要从队头出队;最后要判断当前元素下标是否满足一个窗口长度,因为只有有窗口才可以将当前窗口内的最大值加入结果数组。注意,使用的是双端队列!这样就维护了一个长度为size的单调递减队列,每次都从队头取窗口内的最大值!

vector<int> maxInWindows(const vector<int>& num, unsigned int size) 
{
     vector<int> res;
     // 数组为空 或者 滑动窗口值大于数组长度
     if(num.size()==0||size>num.size())
        return res;
     // 双端队列
     deque<int> dq;
     for(int i=0;i<num.size();i++)
     {
        // 如果当前元素大于之前的 则将之前的元素依次从队尾弹出 从而保证维护的是单调递减队列
        while(!dq.empty()&&num[i]>num[dq.back()])
           dq.pop_back();
        // 将元素下标入队 以便后续判断元素是否在窗口内
        dq.push_back(i);
        // 若队头元素不合法就从队头弹出 双端队列维持长度为size 队首小于窗口左端则过期
        if(i-dq.front()+1>size)
           dq.pop_front();
        // 当满足窗口长度时就将窗口最大值加入结果数组
        if(i>=size-1)
           res.push_back(num[dq.front()]);
     }
     return res;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值