滑动窗口的最大值&&最小值

博客围绕滑动窗口的最大值和最小值问题展开。以LeetCode题目为例,先介绍滑动窗口最大值问题,指出双指针暴力解法会超时,可用队列优化,详细说明了队列操作步骤及代码;还提及滑动窗口最小值问题,只需调整队尾元素删除条件,给出了完整代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️林 子
      🛰️博客专栏:✈️ 小林的算法笔记
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

滑动窗口的最大值

题目摘自leetcode,链接:239. 滑动窗口最大值 - 力扣(LeetCode)

难度:困难

题目描述:

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

输入: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

示例 2 :

输入:nums = [1], k = 1
输出:[1]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

看到这题时,我的第一反应就是双指针暴力解法,用双指针固定住两边的值,随后每次遍历一遍求得最大值。

在这里插入图片描述

但是后面发现超时了,这是因为给的数值范围最大是10万。 而暴力每次循环的复杂度为 O (N - K) * K ,当K为5W,N为10W时,那么复杂度就会变成5W * 5W 。所以就会超时。 而我们可以用队列来优化。

注意!

该队列必须支持尾删功能,我们可以用数组自己模拟一个队列出来。每次往队列插入数据前先检查right指针的数据是否大于队尾元素,如果大于就移除掉队尾元素。还有一个问题就是队列的队头元素所在的下标如果不在滑动窗口内了,那么也要把该元素移除。所以这也就意味着我们的队列必须存储对应数据的下标!!否则无法判断队头元素是否还在滑动窗口内。而当滑动窗口的长度大于等于k时,我们再把数据放进ret数组中。

在这里插入图片描述

所以我们主要是分为以下几个步骤:

1. 判断队头数据是否在滑动窗口内,用left 和 队头存储的下标做比较

2. 用right下标对应的数据和队尾存储下标对应的数据做比较,如果小于就移除队尾数据,一直循环,直到队列为空或者队尾数据大于right下标指向的数据

3. 往队尾push right指向的数据

**4. 如果right - left 长度 >= k -1 ,则把队头的数据加入到返回的数组中 **

5. 移动right和left指针,如果right - left < k -1 , 则不移动left。

代码:

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ret; //返回的数组
        vector<int> queue(nums.size()); //模拟的队列
        int hh = 0 , tt = 0; //队头和队尾,tt指向下一个插入的位置,真正的队尾是tt-1
        int left = 0 ,right = 0; //2个指针固定滑动窗口
        while(right < nums.size()) //right 到达 nums.size() -1,说明滑动窗口到达了边界
        {
            //1. hh < tt 意思队列不为空,left > queue[hh] 说明队头不在滑动窗口内
            while(hh < tt && left > queue[hh]) hh++; //pop掉队头元素
            //2. 如果right指向的元素大于队尾下标指向的元素,那么pop掉队尾,tt指向的是下一个插入位置的值,所以tt - 1 才是队尾
            while(hh < tt && nums[queue[tt - 1]] <= nums[right]) tt--;
            //3. 把right入队
            queue[tt++] = right;  
            //4. 判断窗口大小
            if(right - left >= k - 1)
            {
                //滑动窗口大小正常,把队头指向的值push给right
                ret.push_back(nums[queue[hh]]); 
                //5.移动滑动窗口
                left++ , right++;
            }else right++; //5. 大小不满足只移动right
        } 
        return ret;
    }

运行结果:

在这里插入图片描述

滑动窗口最小值

而滑动窗口的最小值也是一样的,我们只需要让队尾元素大于right下标指向的元素,就进行尾删即可。也就是把上面代码中的while(hh < tt && nums[queue[tt - 1]] <= nums[right]) tt--; 替换成 while(hh < tt && nums[queue[tt - 1]] > nums[right]) tt--;

滑动窗口最小值的完整代码则是:

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ret; //返回的数组
        vector<int> queue(nums.size()); //模拟的队列
        int hh = 0 , tt = 0; //队头和队尾,tt指向下一个插入的位置,真正的队尾是tt-1
        int left = 0 ,right = 0; //2个指针固定滑动窗口
        while(right < nums.size()) //right 到达 nums.size() -1,说明滑动窗口到达了边界
        {
            //1. hh < tt 意思队列不为空,left > queue[hh] 说明队头不在滑动窗口内
            while(hh < tt && left > queue[hh]) hh++; //pop掉队头元素
            //2. 如果right指向的元素小于队尾下标指向的元素,那么pop掉队尾,tt指向的是下一个插入位置的值,所以tt - 1 才是队尾
            while(hh < tt && nums[queue[tt - 1]] > nums[right]) tt--;
            //3. 把right入队
            queue[tt++] = right;  
            //4. 判断窗口大小
            if(right - left >= k - 1)
            {
                //滑动窗口大小正常,把队头指向的值push给right
                ret.push_back(nums[queue[hh]]); 
                //5.移动滑动窗口
                left++ , right++;
            }else right++; //5. 大小不满足只移动right
        } 
        return ret;
    }

运行结果:

在这里插入图片描述

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

林 子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值