← [ BACK_TO_NOTES ]

栈

栈是一种后进先出(LIFO)的数据结构,适合保存“最近进入但尚未完成”的状态;核心操作是 push、pop、peek/top,常用于匹配、嵌套上下文、撤销与延迟处理。

#algorithm #concept #growing #stack

[!info] related notes 算法面试题型 MOC 单调栈 递归 有效括号 删除字符串中的所有相邻重复项 逆波兰表达式求值 基本计算器 最小栈 小行星碰撞 每日温度 柱状图中最大的矩形 接雨水 用栈实现队列 用队列实现栈 原地栈 最长有效括号

栈

一句话定义

栈(Stack)是一种 Last In, First Out(LIFO,后进先出) 的数据结构。

它最稳定的心智模型不是“一摞盘子”,而是:

把最近进入、但还没有处理完的上下文保存起来;需要继续处理时,优先回到最新那一层。

核心操作

push(x)  → 把 x 放到栈顶
pop()    → 移除并返回栈顶
peek()   → 查看栈顶但不移除
empty()  → 是否为空

只允许从同一端进入和离开,是 LIFO 语义成立的关键。

为什么它适合“最近未完成状态”

例如括号:

([{}])

扫描到一个左括号时,它代表一个尚未闭合的上下文。

新的左括号会嵌套在旧上下文里面,因此闭合时必须先处理最近打开的那个:

push (
push [
push {
遇到 } → pop {
遇到 ] → pop [
遇到 ) → pop (

这就是栈比普通数组“碰巧能 push/pop”更重要的语义。

常见问题形态

匹配与嵌套

消除与回退

计算上下文

调用上下文

函数递归时,运行时也会维护调用栈。详见 递归。

栈与递归

递归与显式栈常常可以互相改写:

递归
= Runtime 替你保存“接下来回到哪里、局部状态是什么”

显式栈
= 程序自己保存这些待恢复上下文

这不代表所有递归都应该机械改成栈,但它解释了 DFS、树遍历、表达式解析中两者为什么经常互换。

普通栈与单调栈

单调栈 仍然使用 LIFO 容器,但额外维护“栈内元素具有单调性”的不变量,用于淘汰候选并寻找最近更大/更小或边界。

因此:

普通栈
→ 重点是最近未完成上下文

单调栈
→ 在 LIFO 之上再增加候选单调性约束

单调栈的方向、模板和边界题统一维护在 单调栈,本页不复制第二份模板。

栈与队列

  • 栈:LIFO,优先处理最近进入的未完成状态;
  • 队列:FIFO,优先处理最早进入的待处理状态。

判断时不要只问“能不能实现”,而要问题目的自然处理顺序是什么。

边界与常见错误

  • 空栈时直接读取栈顶;
  • 只会背 push/pop,却没有说明栈里保存的对象代表什么;
  • 遇到“最近更大/更小”仍在普通栈本体里硬套,应转到 单调栈;
  • 把栈当成“万能 DFS 工具”,却没有明确待恢复状态。

学习检查

不看正文回答:

  1. LIFO 描述的究竟是什么约束?
  2. 为什么括号匹配天然适合保存“最近未完成上下文”?
  3. 递归调用与显式栈之间有什么结构对应关系?
  4. 普通栈与单调栈共享什么,又多出了什么不变量?
  5. 栈和队列的核心差异为什么不是 API 名称,而是处理顺序?
  6. 给一个消除类问题,说明栈里应该保存什么、什么时候 pop、pop 后为什么旧元素无需继续保留。
  7. 面对陌生题,能否先说清“栈顶代表什么未完成状态”,再决定是否使用栈?
创建于 2026/3/19 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本