普通队列无法O(1)获取滑动窗口最大值,因需每次遍历导致O(nk);单调队列通过维护严格递减序(存索引、

滑动窗口最大值为什么不能用普通队列
因为普通队列(比如 collections.deque 仅做 FIFO)无法在 O(1) 时间内获取当前窗口内的最大值。每次窗口滑动后重新遍历找最大,时间复杂度会退化到 O(nk),而单调队列能保证队首始终是当前窗口最大值,均摊 O(1) 查询。
关键不是“存数据”,而是“维护递减顺序”:队列从队首到队尾严格单调递减(允许相等时保留较新索引),这样队首永远是最大且未过期的元素。
单调队列的入队逻辑怎么写
入队前必须弹出所有 小于等于 当前元素的队尾值——因为它们既比当前数小,又比当前数早进队、早出队,对后续窗口毫无价值。
示例逻辑(以索引和值共同存储为佳):
立即学习“Python免费学习笔记(深入)”;
while dq and nums[dq[-1]] <= nums[i]:
dq.pop()
dq.append(i)注意: 是关键,否则相同值会残留旧索引导致错误;<code>dq 存的是索引,方便后续判断是否越界。
如何安全地剔除滑出窗口的旧元素
每次取最大值前,检查队首索引是否还在当前窗口内:if dq[0] 就说明它已经左边界之外,必须 <code>popleft()。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
这个判断必须放在「取结果」之前,但可以在「入队」之后或之前,只要保证每次取 nums[dq[0]] 时队首有效即可。
常见错误包括:
- 忘记判断越界,直接取
dq[0]导致索引错误 - 在入队前就删队首,可能误删本该保留的最大值
- 用值而非索引来判断越界,无法处理重复值场景
完整实现要注意哪些边界
窗口大小 k == 1 时,输出就是原数组;k > len(nums) 应返回空列表,但题目通常保证合法输入。真正容易被忽略的是初始化阶段:
前 k-1 个元素只入队不记录结果;第 k 个元素(即索引 k-1)才开始输出第一个最大值。
推荐结构:
for i in range(len(nums)):
# 1. 删过期索引(队首)
# 2. 维护单调性(队尾)
# 3. 若 i >= k-1,记录 nums[dq[0]]队列为空时不用特殊处理——只要输入合法,第一步删完过期元素后,第二步至少会把当前 i 加进去,所以第三步取 dq[0] 永远安全。
实际编码中,deque 的 popleft() 和 pop() 都是 O(1),但频繁调用仍建议用 if dq: 做空检查,避免抛 IndexError。

















