155. 最小栈
LeetCode 1551 · 中等 · 栈
题
设计一个栈,支持 push、pop、top,以及在常数时间内返回栈中最小元素的 getMin。pop/top/getMin 只会在非空栈上调用。
例:push(-2), push(0), push(-3), getMin() → -3, pop(), top() → 0, getMin() → -2。
一句话
栈里存 (值, 截至该层的最小值) 二元组,getMin 就是看栈顶的第二个分量。
关键技巧
把「全局最小值」变成「前缀最小值」。 栈只在顶部增删,某一层以下的内容在它存活期间不会变——所以「从栈底到第 \(k\) 层的最小值」一旦算出就永远有效。入栈时算 \(\min(x, \text{下一层的前缀最小})\) 存起来,\(O(1)\)。
为什么不能只维护一个 min 变量:弹出当前最小值后,不知道次小值是谁,只能 \(O(n)\) 重扫。前缀最小值等于把「每个历史时刻的答案」都存了下来,弹栈就是回到上一个时刻。
辅助栈写法:另开一个栈只在 x <= 当前最小 时压入,弹出时若值相等一起弹——省空间(相等时也必须压,否则重复最小值弹一个就丢了)。复杂度同阶,二元组写法更不易错。
解
class MinStack:
def __init__(self):
self.stack = [] # (val, 截至该层的最小值)
def push(self, val: int) -> None:
cur_min = min(val, self.stack[-1][1]) if self.stack else val
self.stack.append((val, cur_min))
def pop(self) -> None:
self.stack.pop()
def top(self) -> int:
return self.stack[-1][0]
def getMin(self) -> int:
return self.stack[-1][1]
class MinStack:
def __init__(self):
self.stack, self.mins = [], []
def push(self, val: int) -> None:
self.stack.append(val)
if not self.mins or val <= self.mins[-1]: # 相等也要压
self.mins.append(val)
def pop(self) -> None:
if self.stack.pop() == self.mins[-1]:
self.mins.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.mins[-1]
所有操作时间 \(O(1)\),空间 \(O(n)\)。
延伸
- 最小队列不能照搬——队列两端都动,前缀最小值会失效。用单调队列(239. 滑动窗口最大值)或「两个最小栈拼一个队列」。
- 「在数据结构里预存每个时刻的聚合值」是通用招:前缀和(560. 和为 K 的子数组)是同一思路在数组上的版本。
- 另一种 \(O(1)\) 额外空间的写法:栈里存
val - min的差值,负差值表示「这里更新过最小值」——能用,但易溢出(其他语言里)且难读,面试不推荐。
自测
-
LeetCode 155,2026-09-29 读。 ↩