在大屏或电脑上体验更佳——动画和操作区域更宽敞。
输入

栈

栈遵循后进先出(LIFO):push 把元素放到栈顶,pop 取出最后放入的元素。只操作同一端,不移动中间元素,也不用扫描。用本课 Python 列表实现时,这两项操作的摊还时间为 O(1)。

把栈看成叠放的盘子:从顶端放入,也从顶端取走。这个限制适合需要逆序恢复的过程,如函数调用、撤销记录、嵌套括号匹配和深度优先遍历。

栈如何工作

代码用 Python 列表 data 和容量 max 实现有界栈。push(val) 先检查 len(self.data) >= self.max。满栈抛出 Stack Overflow,否则在列表末尾追加 val。pop() 先检查是否为空;空栈抛出 Stack Underflow,否则删除并返回末尾元素。

栈顶就是列表最后一个索引。append 和 list.pop() 都操作这一端,不需要处理下面的元素。

容量上限来自本课实现,不是 LIFO 的要求。嵌入式系统常使用有界栈,普通 Python 列表则可随可用内存扩展。容量滑块的 3–10 只是画布限制。

逐步演示

默认栈为空,容量为 8。用压栈、出栈按钮运行:

  1. push(3):0 < 8,检查通过,栈为 [3],栈顶索引为 0。
  2. push(7):栈变为 [3, 7],新元素位于旧栈顶上方。
  3. push(1):栈变为 [3, 7, 1]。
  4. pop():返回最后加入的 1,留下 [3, 7]。
  5. pop():返回 7,留下 [3]。
  6. push(9):得到 [3, 9],复用原来放 7 的位置。
  7. 连续两次 pop():依次返回 9、3,栈恢复为空。再出栈一次就下溢。

再试满栈:容量为 8 时,第九次压栈在追加前抛出上溢,失败操作不改变状态。

画布将栈画成顶部开口的容器。元素从顶部进入或离开,标题区显示 size 与栈顶值;空栈时隐藏栈顶值,避免残留旧状态。上面的序列给出了完整状态记录。

时间与空间复杂度

操作时间空间
push摊还 O(1)每个元素 O(1)
pop摊还 O(1)—
peekO(1)—
全部存储—O(n)

接口只允许访问栈顶。结合列表末端操作,才得到这里的成本;数据结构的规则与具体存储实现要一起考虑。

边界情况

  • 满栈压入:抛出上溢,状态不变。
  • 空栈弹出:抛出下溢;先检查是否为空,或捕获异常。
  • 单个元素:压入后弹出同一个值,大小为 0 → 1 → 0。
  • 重复值:允许。栈保存顺序,不要求值唯一。

常见错误

  • 把 pop 当成 peek。 pop 会删除元素。只想查看时读取 data[-1],并先处理空栈。
  • 两端混用。 从尾部压入、头部取出就变成了队列。
  • 省略空检查。 Python 的空列表 pop() 抛出异常,JavaScript 数组则返回 undefined;都应按 API 处理。
  • 允许任意中间索引。 调用者直接读取 data[k] 后,接口不再限制为 LIFO。需要这种访问时考虑列表或双端队列。
  • 忽略调用栈。 函数调用压入栈帧,返回时弹出。递归中的栈溢出,是运行时调用栈耗尽。