题目描述
思路解析动画文字版
如果每次查询最小值都遍历整个栈,设计题就失去意义。
当新值小于等于当前最小值,就同步压入辅助栈。等号不能丢:重复最小值要各存一份。
push(-2):-2 是第一个元素,也是当前最小值。两个栈都压入 -2。
push(0):0 大于当前最小 -2,只进数据栈,最小栈不动。
push(-3):-3 更小,同步进入最小栈。现在 getMin 是 -3。
关键帧 · 弹出最小值,min 自动回滚到 -2:这就是最小栈的灵魂。刚才弹出的 -3 正好等于最小栈顶,所以最小栈也跟着弹一个。瞬间,getMin 自动回到上一个最小值 -2——没有重新扫描,因为「每一刻的最小值」早就一层层存在最小栈里了。最小值有历史,就能跟着数据栈一起进退。
(换个小例子)连续两个 -2 · 重复最小值要保留:单独看一个小例子:连续压入两个 -2。两个都是最小值,第二个 -2 也必须进最小栈——所以入栈判断要用「小于等于」而不是「小于」。这样以后弹掉一个 -2,getMin 仍能看到另一个 -2。
回到原例 · getMin() 只看栈顶,O(1):回到刚才 [-2, 0] 那个栈。查询最小值时只读最小栈的栈顶,一步就拿到 -2,所以 getMin 是 O(1),完全不用扫描数据栈。
辅助栈顶永远代表当前数据栈的最小值。
边界三连:重复最小值、弹出最小值、负数,能测出辅助栈是否正确。
雷区实演 · 重复最小值:两个 -2 都是最小值。如果第二个 -2 没进最小栈,弹掉一个后 getMin 就会出错。
三个高频追问。第一,能只用一个栈:每个元素存成「值 + 当时最小」的二元组,一起进出。第二,辅助栈最坏 O(n):元素一直递减时每个都成为新最小、都要记。第三,pop 空栈:LeetCode 一般保证合法,工程里要抛异常或返回约定值。
这题学完不要乱跳,下一步去 对应专题 连刷同类题;卡住时用 AI 答疑追问“为什么这一步成立”,比只背答案更稳。
参考代码
class MinStack: def __init__(self): self.st = [] self.mn = [] def push(self, val): self.st.append(val) if not self.mn or val <= self.mn[-1]: self.mn.append(val) def pop(self): x = self.st.pop() if x == self.mn[-1]: self.mn.pop() def top(self): return self.st[-1] def getMin(self): return self.mn[-1]复杂度
- push/pop/top/getMin:O(1),每个操作只碰栈顶
- 空间复杂度:O(n),最坏每个元素都进入最小栈
可套用的代码模板
最小栈的迁移点不是普通单调栈,而是“数据栈 + 最小值栈”同步维护:push 时可能同步压最小栈,pop 时可能同步弹最小栈。
class MinStack: def __init__(self): self.data = [] self.mins = [] def push(self, val): self.data.append(val) if not self.mins or val <= self.mins[-1]: self.mins.append(val) def pop(self): x = self.data.pop() if x == self.mins[-1]: self.mins.pop() def top(self): return self.data[-1] def getMin(self): return self.mins[-1]易错点
面试追问把动画讲成自己的话
追问能只用一个栈吗?
追问辅助栈最坏为什么是 O(n) 空间?
追问pop 空栈要处理吗?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
逆波兰表达式求值
LeetCode 150 · 中等 · 沿着 栈 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题