滑动窗口的最大值

力扣239题

题目描述:给你一个整数数组 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]n

拿到这道题,我的第一想法就是:

是否可以设置一个最大值的标识符max1用来记录当前窗口的最大值和其下标,用若出现了一个新的元素,该元素的值大于max1,则max1等与该元素,但是存在一个问题,当max1的下标滑出该窗口时,如何去找到下一个最大值?

于是该方法不太能行得通。

双端队列法:

为了解决这个问题,我们可以使用双端队列(deque)来存储滑动窗口中的元素索引。这个双端队列有一个重要的性质:他保存的元素下标是从队首到队尾保持递增的顺序,但是它的元素下标对应的值始终从队首到队尾保持递减的顺序。也就是说,队首元素是当前滑动窗口中的最大值(nums[ qu.front() ] 是最大值)。

下面是使用双端队列解决这个问题的算法步骤:

  1. 初始化一个空的双端队列。
  2. 遍历数组 nums,对于每个索引 i:
    • 如果双端队列不为空,并且队首元素的索引已经不在当前滑动窗口内(即索引小于 i - k + 1),则从队首弹出该元素。
    • 遍历双端队列,从队尾开始,移除所有小于 nums[i] 的元素索引。这一步是为了保证双端队列的递减性质。
    • 将当前元素索引 i 添加到双端队列的队尾。
    • 如果当前滑动窗口已经完整(即 i >= k - 1),则将队首元素(即当前滑动窗口中的最大值)添加到结果数组中。
  3. 返回结果数组。

代码:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(k == 1){
            return nums;
        }
        
        int len = nums.size();
        //双端队列dp保存元素的下标。
        deque<int> qu;
        vector<int> ans;

        for(int i = 0 ; i < k - 1 ;i++){
            while(!qu.empty() && nums[i] >= nums[qu.back()]){
                qu.pop_back();
            }
            qu.push_back(i);
        }
        for(int i = k - 1 ; i < len ;i++){
            //1、先判断队列的头部元素是否不在窗口内,若小于i-k则出队对头元素
            while(!qu.empty() && qu.front() <= i-k){
                qu.pop_front();
            }
            //2、再将当前要入队的元素与队尾元素比较,若队尾元素的值小于当
            //前要入队的元素,则将队尾元素出队。
            while(!qu.empty() && nums[i] >= nums[qu.back()]){
                qu.pop_back();
            }
            //3、当前的对头元素则是窗口的最大值,保存到结果数组中
            //4、将当前元素压入队尾
            qu.push_back(i);
            ans.push_back(nums[qu.front()]);
        }
        return ans;
    }
};

相关题目:力扣1696

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值