题解 | 滑动窗口的最大值

滑动窗口的最大值

https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788?tpId=295&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=%2Fexam%2Foj%3FquestionJobId%3D10%26subTabName%3Donline_coding_page

#include <vector>
#include <deque>
using namespace std;

class Solution {
  public:
    vector<int> maxInWindows(vector<int>& num, int size) {
        vector<int> result;
        if (size == 0 || size > num.size()) {
            return result;
        }

        deque<int> dq; // 存储索引,保证队列中的索引对应的元素是单调递减的

        for (int i = 0; i < num.size(); i++) {
            // 移除超出当前窗口范围的索引
            if (!dq.empty() && dq.front() < i - size + 1) {
                dq.pop_front();
            }

            // 维护双端队列的单调递减特性
            // 从队列尾部移除所有小于当前元素的索引
            while (!dq.empty() && num[dq.back()] < num[i]) {
                dq.pop_back();
            }

            // 将当前索引加入队列
            dq.push_back(i);

            // 当窗口完全形成时(即i >= size-1),记录当前窗口的最大值
            if (i >= size - 1) {
                result.push_back(num[dq.front()]);
            }
        }

        return result;
    }
};

算法思路详解:
1.边界情况处理:
如果窗口大小为0或大于数组长度,直接返回空结果
2.双端队列的使用:
使用双端队列存储数组元素的索引而不是值
队列中的索引对应的元素值保持单调递减的顺序
队列前端始终是当前窗口的最大值的索引
3.遍历过程中的操作:
移除过期元素:检查队列前端的索引是否还在当前窗口范围内,如果不在就移除
维护单调性:从队列尾部移除所有对应值小于当前元素的索引,因为它们不可能成为后续窗口的最大值
添加当前元素:将当前元素的索引加入队列尾部
4.记录结果:当遍历到足够形成完整窗口的位置时,将队列前端的值(当前窗口的最大值)加入结果

时间复杂度分析:
每个元素最多被压入和弹出队列各一次
因此总体时间复杂度为O(n)

空间复杂度分析:
双端队列最多存储size个元素
结果数组存储n-size+1个元素
因此空间复杂度为O(n)
  
  关键变量含义:
i:当前遍历到的数组索引
size:滑动窗口大小
dq.front():双端队列前端的索引(当前窗口最大值的候选索引)
i - size + 1:当前窗口的起始位置

也可以分成两部分循环,因为只有i>=k是才会需要移除超出当前窗口范围的索引
#include <vector>
#include <deque>

using namespace std;

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;
        if (k == 0 || k > nums.size()) return res;
        deque<int> dq;
        for (int i = 0; i < k; ++i) {
            while (!dq.empty() && nums[dq.back()] < nums[i]) {
                dq.pop_back();
            }
            dq.push_back(i);
        }
        res.push_back(nums[dq.front()]);
        for (int i = k; i < nums.size(); ++i) {
            while (!dq.empty() && i - dq.front() + 1 > k) {
                dq.pop_front();
            }
            while (!dq.empty() && nums[dq.back()] < nums[i]) {
                dq.pop_back();
            }
            dq.push_back(i);
            res.push_back(nums[dq.front()]);
        }
        return res;
    }
};

栈/堆/队列 文章被收录于专栏

操作只能在栈顶进行 栈应用场景 函数调用栈 浏览器前进后退 括号匹配检查 表达式求值 变种队列 双端队列 (Deque):两端都可以入队和出队 优先队列 (Priority Queue):按优先级出队 循环队列:固定大小的循环使用空间 队列应用场景 消息队列 任务调度 广度优先搜索 打印任务队列 堆应用场景 优先队列实现 堆排序 Top K 问题 定时任务调度

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务