LeeCode_239. 滑动窗口最大值(优先队列+双端单调队列)

这篇博客介绍了如何利用优先队列(大顶堆)和双端队列(deque)来解决LeetCode上的滑动窗口最大值问题。两种方法分别通过维护一个包含窗口内元素及其下标的队列,动态更新最大值。在滑动窗口移动时,队列不断调整以保持窗口内的最大值。这两个高效的数据结构解决方案能帮助我们在线性时间内找到每个窗口的最大值。
摘要由CSDN通过智能技术生成

一、介绍

1.题目描述

题目链接:https://leetcode-cn.com/problems/sliding-window-maximum/

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

  • 返回滑动窗口中的最大值。

注意事项:

  • 如果 k = 1,可以直接返回

2.测试样例

[1,3,-1,-3,5,3,6,7]
3 
# [3,3,5,5,6,7]

[1]
1 
# [1]

[1,-1]
1
# [1,-1]

[9,11]
2
# [11]

[4,-2]
2
# [4]

[7,2,4]
2
# [7,4]

二、题解

1、优先队列🔴

  1. 创建一个优先队列(大顶堆),记录数值和对应下标。
  2. 先对第一个窗口中的内容进行处理,加入队列中,往 ans 中加入第一个最大元素。
  3. 对于接下来的窗口内容,依次插入,判断当前最大值是否在窗口中
  • 如果在窗口中,加入到答案
  • 如果不在,删除该堆顶

参考链接:https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetco-ki6m/

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(k==1) return nums;
        int n=nums.size();
        // q 为优先队列,ans 记录答案数组
        priority_queue<pair<int,int>> q;
        // 处理第一个窗口
        for(int i=0;i<k;i++) q.emplace(nums[i],i);
        // 加入第一个元素
        vector<int> ans={q.top().first};
        // 遍历之后的窗口
        for(int i=k;i<n;i++){
            q.emplace(nums[i],i);
            // 如果在滑动窗口外,弹出
            while(q.top().second<=(i-k)) q.pop();
            // 记录当前窗口中的最大值
            ans.push_back(q.top().first);
        }
        return ans;
    }
};

image-20210817133622346

2、双端单调队列🟢

  1. 创建一个双端队列,遍历插入当前数的下标,为保证递增:
    • 若当前数比较大,弹出队列中所有小于当前数的下标
    • 否则直接插入
  2. 第一个窗口遍历完,队列头就是第一个答案
  3. 遍历之后窗口时
    • 若队列头下标不在窗口内,则弹出队头
    • 若当前数比较大,弹出队列中所有小于当前数的下标
    • 否则直接插入
    • 最后剩下的队列头就是当前窗口的最大值下标

参考链接:https://leetcode-cn.com/problems/sliding-window-maximum/solution/zhe-hui-yi-miao-dong-bu-liao-liao-de-hua-7fy5/

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(k==1) return nums;
        int n=nums.size();
        // 双端队列
        deque<int> q;
        // 处理第一个窗口
        for(int i=0;i<k;i++){
            // 如果队列内的数小于当前数,弹出
            while(!q.empty()&&nums[i]>=nums[q.back()]){
                q.pop_back();
            }
            // 插入当前数
            q.push_back(i);
        }
        // ans 记录答案
        vector<int> ans={nums[q.front()]};
        // 处理接下来的窗口
        for(int i=k;i<n;i++){
            // 下标超出窗口,弹出
            while(q.front()<=i-k) q.pop_front();
            // 如果队列内的数小于当前数,弹出
            while(!q.empty()&&nums[i]>=nums[q.back()]){
                q.pop_back();
            }
            q.push_back(i);
            // 当前队列头为当前窗口内最大值下标
            ans.push_back(nums[q.front()]);
        }
        return ans;
    }
};

image-20210817141931707

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值