剑指 Offer 59 - I. 滑动窗口的最大值


前言


一、剑指 Offer 59 - I. 滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
 

提示:

你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

二、解决方法

1.引入库

代码如下:

方法一

class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    if(k == 1) return nums;
    int n = nums.size();
    vector<int> res;
    if(n == 0) return res;
    res.resize(n-k+1);
    res[0] = nums[0];
    for(int i = 1;i<k;i++)
    {//计算 0 到 k 的最大值存入res[0]
        res[0] = max(res[0],nums[i]);
    }
    for(int i = k;i<n;i++)
    {
        if(res[i-k] == nums[i-k])
        {
            res[i-k+1] = nums[i];
            for(int j = i-k+1;j<i;j++)
            {
                res[i-k+1] = max(nums[j],res[i-k+1]);
            }
        } 
        else res[i-k+1] = max(res[i-k],nums[i]);
    }
    return res;
    }
};

作者:an-an-jing-jing-6
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/yong-shi-8-ms-ji-bai-liao-9995-nei-cun-1-miqq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法二 双向队列(容易理解 )

解题思路
关键就是用双向队列模拟实现了滑动窗口的过程,同时将这个队列维护成了一个单调队列

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {

        vector<int>res;
        deque<int>dp;
        int len = nums.size();
        if(k<=0 || len<=0 || len<k)
            return res;
        for(int i=0;i<len;i++){

            while(!dp.empty() && nums[i] >= nums[dp.back()]){
                dp.pop_back();//将队列中比刚进入的值小的值弹出去,因为最大值不可能在他们中间产生
            }
            dp.push_back(i);//将这个值加入到单调队列中合适的位置
            if(i >= k-1){//从第一个窗口长度开始处理
                
                res.push_back(nums[dp.front()]);
                if(i-k+1 == dp.front()){//如果现在的最值等于窗口的第一个值,那么要弹出
                    dp.pop_front();
                }
            }
        }
        return res;
    }
};

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

方法三 单调队列

思路:
因为题目要求返回滑动窗口的最大值,而滑动窗口的实质相当于一个队列,每滑动一次都会有窗口左端(相当于队首)的元素出队,窗口右端(相当于队尾)有新的元素入队。

题目包含min函数的栈为通过维护一个单调栈来实现以O(1)的时间复杂度来返回最小值,可以发现实质与该题类似,不过是把栈变成了队列,并且把返回最小值换成了返回最大值。

因此考虑维护一个非严格的单调增的队列来实现最大值的返回。因为滑动窗口会导致队首元素出队,因此考虑使用双端队列实现单调增队列。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;
        if(nums.empty()) // 特殊情况的处理
            return res;
        deque<int> deQ; // 维护一个双端队列
        /* 滑动窗口大小小于k时*/
        for(int i=0; i<k; i++){
            while(!deQ.empty() && deQ.back()<nums[i]) // 始终保持队首元素为最大值
                deQ.pop_back();
            deQ.push_back(nums[i]);
        }
        res.push_back(deQ.front());
        /* 形成大小k的滑动窗口 */
        for(int i=k; i<nums.size(); i++){
            if(deQ.front() == nums[i-k])
                deQ.pop_front();
            while(!deQ.empty() && deQ.back() < nums[i]) // 始终保持队首元素为最大值
                deQ.pop_back();
            deQ.push_back(nums[i]);
            res.push_back(deQ.front());
        }

        return res;
    }
};

作者:yi-ban-shi-min-young
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/cdan-diao-dui-lie-by-yi-ban-shi-min-youn-yndk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.解题思路(链接视频讲解)

1.想将我们第一个窗口的所有值存入单调双端队列中,单调队列里面的值为单调递减的。如果发现队尾元素小于要加入的元素,则将队尾元素出队,直到队尾元素大于新元素时,再让新元素入队,目的就是维护一个单调递减的队列。

2.我们将第一个窗口的所有值,按照单调队列的规则入队之后,因为队列为单调递减,所以队头元素必为当前窗口的最大值,则将队头元素添加到数组中。

3.移动窗口,判断当前窗口前的元素是否和队头元素相等,如果相等则出队。

4.继续然后按照规则进行入队,维护单调递减队列。

5.每次将队头元素存到返回数组里。

5.返回数组

作者:chefyuan
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/yi-shi-pin-sheng-qian-yan-shuang-duan-du-mbga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


总结

以上就是今天要讲的内容
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WeSiGJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值