单调队列入门实践笔记

1023 字
5 分钟
单调队列入门实践笔记

单调队列入门实践笔记#

最近复习滑动窗口相关题目时,又遇到了“每个窗口里的最大值”这个问题。最直接的想法是每移动一次窗口,就重新扫描窗口里的所有元素。但如果数组很长、窗口也不小,这种做法会重复比较很多次。

单调队列就是用来减少这种重复工作的。它本质上还是一个队列,只是队列里会维护一个顺序,让队头始终能代表当前窗口里的最优答案。

1、先看暴力解法的问题#

假设数组是:

nums = [1, 3, -1, -3, 5, 3, 6, 7]
k = 3

每个长度为 3 的窗口最大值依次是:

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

暴力写法很容易理解:

def max_sliding_window(nums, k):
ans = []
for i in range(len(nums) - k + 1):
ans.append(max(nums[i:i + k]))
return ans

但这里每个窗口都重新 max 一次。窗口之间明明有大量重叠元素,却没有复用之前的信息。

2、队列里应该保存什么#

单调队列通常保存下标,而不是直接保存数值。原因是我们既要比较大小,也要判断元素是否已经滑出窗口。

比如当前遍历到下标 i,窗口左边界是:

left = i - k + 1

如果队头下标小于 left,说明它已经不在窗口里,需要弹出。

3、为什么要保持单调递减#

我们想快速得到窗口最大值,所以队头应该对应当前窗口中最大的数。为了做到这一点,队列里的值可以保持从大到小。

当新元素 nums[i] 进来时,如果队尾对应的值比它小,那么队尾元素以后也不可能成为最大值:

  • 它比新元素小。
  • 它还比新元素更早离开窗口。

所以可以直接从队尾弹掉。

while q and nums[q[-1]] <= nums[i]:
q.pop()

这一步是单调队列的核心。

4、完整代码#

用 collections.deque 写起来比较清楚:

from collections import deque
def max_sliding_window(nums, k):
q = deque()
ans = []
for i, x in enumerate(nums):
left = i - k + 1
while q and q[0] < left:
q.popleft()
while q and nums[q[-1]] <= x:
q.pop()
q.append(i)
if i >= k - 1:
ans.append(nums[q[0]])
return ans

这里的 q[0] 始终是当前窗口最大值的下标。只有当窗口已经形成,也就是 i >= k - 1 时,才把答案加入列表。

5、用一个窗口走一遍#

还是看这个数组:

nums = [1, 3, -1]

遍历到 1 时,队列为空,直接加入下标 0。

遍历到 3 时,队尾的 1 比 3 小,弹出下标 0,再加入下标 1。此时队头就是 3。

遍历到 -1 时,队尾的 3 比 -1 大,不弹出,加入下标 2。窗口形成后,队头仍然是下标 1,对应最大值 3。

这个过程说明,队列里不需要保存窗口内所有元素,只保留“未来可能成为最大值”的候选下标。

6、常见边界检查#

写单调队列时,我会特别检查这几个地方:

  • 队列保存的是下标,不是值。
  • 先移除滑出窗口的队头。
  • 再从队尾移除不可能成为答案的元素。
  • 窗口长度达到 k 后再记录答案。
  • 比较时使用 < 还是 <= 要保持一致。

在滑动窗口最大值里,我通常写 nums[q[-1]] <= x,这样相同值时保留更新的下标,过期判断会更自然。

小结#

单调队列可以理解为“带淘汰规则的候选答案队列”。它不是为了保存所有窗口元素,而是为了让队头随时代表当前窗口的最大值。

对我来说,掌握这个结构的关键是两句话:过期的下标从队头走掉,更弱的候选从队尾删掉。只要这两步写清楚,滑动窗口最大值这类题就会稳定很多。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

单调队列入门实践笔记
https://blog.tjcheng.cn/posts/daily/2026-07-02/queue-monotonic-note/
作者
TengJinCheng
发布于
2026-07-02
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
TengJinCheng
Hello, I'm Teng.
公告
欢迎来到TengJinCheng工作铺!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
157
分类
7
标签
114
总字数
109,939
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.4
文章许可
CC BY-NC-SA 4.0

文章目录