LC 239 滑动窗口最大值:从暴力超时到单调队列一遍过

刷滑动窗口的题,你是不是上手就想写两层循环?我当初也是这么干的,结果这道 hard 题一提交直接超时,给我整懵了。今天就聊聊怎么用「单调队列」把这道题轻松拿捏。

题目说个明白

题目其实不复杂。给你一个整数数组,一个大小为 k 的窗口从左往右慢慢滑,每次挪一格。你要返回每个窗口里的最大值,拼成一个新数组。

我踩过的第一个坑:暴力法

最开始我思路特别直白。窗口滑到哪,我就遍历哪 k 个数字找最大值。写起来两分钟,跑示例也没问题,一提交就超时。毕竟 O (n*k) 的时间复杂度,数组一长根本顶不住。

后来我慢慢反应过来。每次窗口只移动一格,只走一个旧元素、来一个新元素。为了找最大值重新扫一遍窗口,也太浪费了。有没有什么结构,能直接告诉我当前窗口最大值是谁?这时候就轮到单调队列出场了。

单调队列到底是啥

我们要维护的是一个单调递减的队列。重点是,队列里存的不是数值,是元素的下标。

这么做有两个好处:队头永远是当前窗口最大值的下标,拿结果直接用。也方便判断队头元素有没有滑出窗口的左边界。

具体怎么操作

遍历数组的时候,每来一个新元素,先做第一件事。把队列尾部所有对应值比当前元素小的下标,全弹出去。你想啊,这些数既比新来的小,还比它先离开窗口,以后不可能再当最大值了,留着纯纯占位置。弹完就把当前下标加到队尾。

接着做第二件事,检查队头。窗口左边界是 i - k + 1,如果队头下标小于左边界,说明它已经滑出去了,直接从队头删掉。

最后,当窗口正式形成的时候(也就是 i >= k-1),把队头对应的值丢进结果数组就 ok。

拿示例里的 [1,3,-1,-3,5,3,6,7],k=3 走两步你就懂了。

  • i=0,队列为空,加 0,队列:[0]
  • i=1,3 比 1 大,弹出 0,加 1,队列:[1]
  • i=2,-1 比 3 小,加 2,队列:[1,2]这时候窗口刚好形成,队头是 1,对应值 3,放进结果。后面的步骤都是一个逻辑,顺下来就行。

代码实现

直接上 JavaScript 代码,注释我都标好了,都是我当初踩过的坑。

javascript 复制代码
var maxSlidingWindow = function(nums, k) {
    const res = [];
    const deque = []; // 单调递减队列,存元素下标
    const n = nums.length;

    for (let i = 0; i < n; i++) {
        // 队尾元素比当前小就弹出,维持递减
        // 别漏了等于的情况,不然重复元素会出问题
        while (deque.length && nums[i] >= nums[deque.at(-1)]) {
            deque.pop();
        }
        deque.push(i);

        const left = i - k + 1; // 窗口左边界
        // 队头已经滑出窗口,从队头移除
        if (deque[0] < left) {
            deque.shift();
        }

        // 窗口形成后,收集当前最大值
        if (left >= 0) {
            res.push(nums[deque[0]]);
        }
    }
    return res;
};

这里我必须提一句,队列一定要存下标。我一开始图省事存数值,结果边界处理全乱套,卡了半小时才反应过来。

复杂度咋样

时间复杂度 O (n),每个元素最多进队一次、出队一次,效率拉满。空间复杂度 O (k),队列最多装 k 个元素。

最后唠两句

这道题本质上考的就是滑动窗口 + 单调队列的组合。核心是理解 "为什么要维护单调递减"------ 把不可能成为最大值的元素提前淘汰掉。我踩过的坑还挺多,存值不存下标、漏了等于的情况、忘了检查队头出界...... 说多了都是泪。

你第一次做这题是直接想到单调队列,还是跟我一样先写了暴力?有没有在什么奇怪的地方踩过坑?评论区唠唠,我每条都会看。觉得文章有用的话,点个赞让更多小伙伴看到呀~

相关推荐
Mh2 小时前
虚拟滚动真的比普通滚动性能更好吗?
前端·javascript·性能优化
.道阻且长.2 小时前
9.LeetCode算法习题讲解--滑动窗口--无重复字符的最长字串
算法·leetcode·职场和发展·哈希算法
ltl3 小时前
HNSW:图索引如何击败树索引
算法
曹牧5 小时前
C#:数字的定义和表示方式
算法·c#
kyriewen5 小时前
我把 AI 写的并发请求控制器手写了一遍——3 个语义我当时根本讲不清
前端·javascript·面试
_codemonster5 小时前
Vue中的ref和reactive到底在干嘛
前端·javascript·vue.js
Nil2085 小时前
leetcode 138随机链表的复制
算法·leetcode·链表
张元清6 小时前
React useEventListener Hook:类型安全的 DOM 事件监听 (2026)
javascript·react.js
weixin_461408587 小时前
npm npx yarn
开发语言·前端·javascript