PrepStack · Markdown HTML

单调队列维护窗口极值

栈、单调栈与单调队列 · HTML

数学 / 逻辑形态

deque 索引按位置递增、对应值按目标方向单调。处理 i 时先/后清除 index≤i-k 的过期头,并从尾部弹出不优于 a[i] 的候选,再压 i。

识别信号

容易误判的相似信号

SignalWhy it is different
最近更大元素没有窗口过期时单调栈更直接。
窗口和/频次可逆聚合用普通滑窗即可,不需要支配淘汰。

核心不变量

deque 中索引严格递增且都在当前窗口内;其值按目标方向单调,队首是当前窗口极值,每个被尾弹的索引在未来共同窗口中永不优于新索引。

状态、转移与处理顺序

ItemDefinition
State索引 deque、当前 i、窗口宽 k,以及输出/DP。
Transition移除队首过期索引;从队尾弹出被当前值支配者;压入 i;窗口成熟后读取队首值。
Frontier / order当前窗口内仍有可能在现在或未来成为极值的 Pareto 候选链。

Python 骨架

from collections import deque

def window_max(values, k):
    if k <= 0 or k > len(values):
        return []
    dq, answer = deque(), []
    for i, value in enumerate(values):
        while dq and dq[0] <= i - k:
            dq.popleft()
        while dq and values[dq[-1]] <= value:
            dq.pop()
        dq.append(i)
        if i >= k - 1:
            answer.append(values[dq[0]])
    return answer

正确性思路

  1. 过期清理保证 deque 中每个索引属于当前窗口。
  2. 若旧候选值≤新值且位置更早,它会更早过期且从不更优,因此尾弹安全。
  3. 剩余值单调,队首既未过期又不被任何候选支配,正是窗口最大值。

复杂度

TimeSpaceParameters
O(n)O(k)每个索引入队一次,从头或尾出队至多一次;deque 最多保存窗口内 k 个索引。

边界条件

错误模式

PatternWhy it fails / fix
deque 存值不存索引无法判断何时过期,重复值也难区分。
过期条件写成 < i-k索引 i-k 已不在 [i-k+1,i],应使用 <=。
把尾部单调方向写反求最大值应从尾部移除不大于新值的候选。

选择与排除规则

相关模板

RelationTemplateBoundary
极值状态组件定长滑动窗口固定窗统计从可逆和升级到不可逆 min/max。
极值/前缀边界增强可变长滑动窗口需在窗口内维护极值或选择最优历史前缀。
有序候选替代Top-K 有界堆需要多个优先级候选而非单调支配链。
同数据结构不同不变量0-1 BFS勿把优先级 deque 与窗口极值 deque 混为一谈。

原创微型例子

环节内容
Result两个窗口最大值为 [3,3]。
Setup值 [2,1,3,2],k=3,求每窗最大值。
Trace到 3 时从尾部弹 1、2,只留索引 2;下一步加入 2 但 3 仍在队首。

Recall prompts

我的补充