不能只用一个变量记录栈顶最大值,因为pop()后无法恢复历史最大值;辅助栈需在x>=当前最大值时入栈,pop()时仅当弹出值等于辅助栈顶才同步弹出,且必须用>=而非>保证重复最大值不丢失。

为什么不能只用一个变量记录栈顶最大值
因为栈是后进先出结构,当执行 pop() 操作时,如果当前最大值被弹出,你无法回溯到上一个最大值——单个变量没有“历史快照”能力。比如压入 [3, 1, 4, 2],最大值变成 4;再 pop() 一次后栈为 [3, 1, 4],此时最大值仍是 4;但再 pop() 一次,栈变 [3, 1],最大值应退回到 3,而单变量早已被覆盖成 4,无法还原。
辅助栈怎么同步更新才不漏不冗
关键原则:辅助栈(记为 max_stack)只在「新元素 ≥ 当前最大值」时才压入。这样它天然保持非严格递减,且每个元素代表“从该位置到底部区间内的最大值”。pop() 时,仅当主栈弹出的值等于 max_stack[-1],才同步弹出辅助栈顶。
- 压入
5→ 主栈:[5],max_stack为空,直接压入 →[5] - 压入
3→3 ,不压入 <code>max_stack→max_stack仍为[5] - 压入
5→5 == 5,压入 →max_stack = [5, 5] -
pop()弹出5,与max_stack[-1]相等 → 同步弹出,max_stack变回[5]
Python 实现要注意的边界和类型细节
用 list 模拟栈时,append() 和 pop() 是 O(1),但需注意空栈调用 top() 或 getMax() 会触发 IndexError。生产环境建议显式检查 if not self.stack: 并抛出 ValueError。另外,Python 中 float('-inf') 不适合初始化 max_stack,因为你要比的是实际入栈元素,不是靠哨兵值兜底——辅助栈初始就应为空,首次 push() 才开始建值。
class MaxStack:
def __init__(self):
self.stack = []
self.max_stack = []
def push(self, x: int) -> None:
self.stack.append(x)
if not self.max_stack or x >= self.max_stack[-1]:
self.max_stack.append(x)
def pop(self) -> None:
if not self.stack:
raise ValueError("pop from empty stack")
x = self.stack.pop()
if self.max_stack and x == self.max_stack[-1]:
self.max_stack.pop()
def top(self) -> int:
if not self.stack:
raise ValueError("top from empty stack")
return self.stack[-1]
def getMax(self) -> int:
if not self.max_stack:
raise ValueError("getMax from empty stack")
return self.max_stack[-1]
性能和空间开销的真实代价
时间上所有操作都是 O(1),但空间最坏情况是 O(n):比如压入单调递增序列 [1,2,3,4,5],辅助栈也会存满 5 个元素。这不是 bug,是设计必然——你必须保留每个“局部峰值”的上下文才能正确回退。如果业务中最大值变化极少(例如大量重复小值 + 偶尔大值),这个空间效率其实很高;但若数据随机且起伏频繁,辅助栈长度会接近主栈,这时得接受这个 trade-off。
真正容易被忽略的是:辅助栈必须用 >= 而不是 > 判断是否入栈。漏掉等号会导致相同最大值多次出现时,辅助栈只留一个,后续 pop() 就会提前清空最大值记录,查出错误结果。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











