用deque而非list实现单调栈,因deque的append/pop为o(1)且支持高效双端操作;list虽末尾操作也是o(1),但误用insert(0,x)或pop(0)会退化为o(n)。

为什么不用 list 而要用 deque 实现单调栈?
因为 list 在头部或中间插入/删除是 O(n) 操作,而单调栈的核心操作(如弹出栈顶直到满足单调性)常需频繁从末尾增删,deque 的 append() 和 pop() 都是 O(1),且内存连续性更好。但注意:如果只做「后进先出」且不涉及随机索引,list 其实也够用——它的 append() 和 pop() 同样是 O(1),底层是动态数组预留空间。真正踩坑点在于误用 insert(0, x) 或 pop(0),那会退化成 O(n)。
实操建议:
- 优先用
deque,尤其当逻辑可能扩展为双端维护(比如滑动窗口+单调性)时 - 若纯单向单调栈(仅右端进出),
list更轻量、更易调试,别被“deque 更快”误导 - 避免在
list上写stack.pop(0)—— 这不是单调栈,这是自找 O(n) 延迟
如何用 deque 写一个通用的单调递减栈?
关键不是存什么,而是每次 push 前「把破坏单调性的老元素全踢掉」。以递减栈为例:新元素比栈顶大,就不断 pop() 直到栈空或栈顶 ≥ 新元素。
示例代码(带注释):
from collections import deque <p>def monotonic_decreasing_stack(nums): stack = deque() for x in nums: while stack and stack[-1] </p><h1>输入 [3,1,4,1,5] → 输出 [5]</h1><p></p>
注意点:
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
-
stack[-1]是 O(1) 访问,deque支持负索引 - 别写
stack[0]来判断——那是最老元素,和单调性无关 - 如果需要同时记录原下标(如「下一个更大元素」题),存元组
(value, index)更安全
list 实现单调栈时最容易忽略的边界问题
用 list 写单调栈,90% 的 bug 出现在空栈检查和比较逻辑上。常见错误现象:IndexError: list index out of range 或漏删该删的元素。
正确写法必须显式判空:
stack = []
for x in nums:
while stack and stack[-1] <p>错误写法(危险):</p><pre class="brush:python;toolbar:false;">while stack[-1] <p>其他要点:</p>
- 比较运算符方向决定单调性:用
是递减栈,用 <code>>是递增栈 - 如果输入含重复值,需明确是否允许相等——通常单调栈定义中「严格单调」用
/<code>>,「非严格」用/<code>>= -
list的len(stack)是 O(1),放心用,别为省一次长度计算去缓存变量
什么时候该放弃单调栈,改用其他结构?
单调栈本质是在线维护一个「最值候选队列」,但它不支持随机查询、不支持区间修改、不能回溯历史状态。一旦需求出现以下任一情况,就得换思路:
- 需要查「栈中第 k 大元素」——考虑
SortedList(来自 sortedcontainers) - 要支持「撤销上一步 push/pop」——加个
history = []手动记操作,别指望栈自己保存 - 输入是流式数据且内存受限,还要支持「查询过去 N 个元素的最大值」——用
deque+ 单调队列(双端维护)更合适 - 单调性依赖多个字段(如先按价格降序、价格相同时按时间升序)——单纯栈难表达,得上堆或自定义比较器
真正容易被忽略的是:单调栈解决不了「跨段依赖」问题。比如「每个元素右边第一个比它小的数」能做,但「每个元素右边最小的数(不管位置)」就不能只靠栈——后者需要预处理或线段树。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










