栈和队列的典型应用

1429 字
7 分钟
栈和队列的典型应用
Warning

含AI生成内容

栈和队列的典型应用#

括号匹配#

为什么用栈#

括号的嵌套规则要求最后出现的左括号最先被匹配,与栈的 LIFO 特性完全一致。

算法流程#

  1. 初始化空栈
  2. 从左到右扫描表达式
  3. 左括号 ([{:入栈
  4. 右括号 )]}:若栈空则失败(右括号多余),否则弹栈配对检查
  5. 扫描结束后栈为空则成功,否则失败(左括号多余)

三种失败情况(⭐常考)#

失败类型示例检测时机
括号不匹配{[)}弹栈比较时
右括号多余())遇到右括号时栈已空
左括号多余(()扫描结束栈非空

复杂度#

指标复杂度
时间复杂度O(n) — 每个字符扫描一次
空间复杂度O(n) — 最坏全为左括号

表达式求值#

三种表达式对比#

类型写法示例(a + b * c
中缀表达式运算符在操作数中间a + b * c
前缀表达式(波兰式)运算符在操作数前面+ a * b c
后缀表达式(逆波兰式)运算符在操作数后面a b c * +

后缀表达式不需要括号,运算顺序由运算符出现的先后唯一确定。

中缀转后缀#

使用运算符栈,按优先级规则决定何时弹出:

  1. 操作数 → 直接输出
  2. ( → 入栈
  3. ) → 弹出栈顶直到 (( 弹出不输出
  4. 运算符 → 弹出栈中优先级 当前运算符的运算符,再入栈
  5. 结束 → 弹出栈中剩余运算符

运算符优先级

运算符栈外优先级栈内优先级
(最高最低
* /
+ -
)最低

⚠️ 易错:左括号 ( 栈外优先级最高(保证能入栈),栈内优先级最低(保证不会被普通运算符弹出)。只有 ) 才能弹出它。理解这一点是正确模拟中缀转后缀的关键。

⚠️ 易错:步骤 4 中的 不能改成 >。如果改成严格大于,相同优先级的运算符不会被弹出,会破坏左结合性。例如 a - b + c 应该先算 a-b,如果 + 不弹出 -,就变成先算 b+c 了。

手算示例a + b * c - (d / e)a b c * + d e / -

后缀表达式求值#

使用操作数栈

  1. 操作数 → 入栈
  2. 运算符 → 弹出两个操作数,先弹出的是右操作数,后弹出的是左操作数,计算结果压回栈
  3. 结束 → 栈顶为最终结果

⚠️ 易错:弹出顺序是先右后左。对于减法和除法(不满足交换律),弄反顺序会得到完全不同的结果。例如后缀 5 3 - 应计算 5 - 3 = 2,弄反就变成 3 - 5 = -2

复杂度#

操作时间复杂度
中缀转后缀O(n)
后缀求值O(n)
空间复杂度O(n)

栈在递归中的应用#

递归的两个要素#

  1. 递归表达式(递归体):问题可分解为规模更小的同类子问题
  2. 边界条件(递归出口):存在不需要递归就能直接求解的最小规模
int Factorial(int n) {
if (n == 0 || n == 1) // 边界条件
return 1;
return n * Factorial(n - 1); // 递归表达式
}

递归工作原理:系统栈#

每次函数调用时,系统在栈中分配一个栈帧(活动记录),保存:

  • 返回地址:调用结束后回到哪里
  • 局部变量:本次调用的局部数据
  • 参数值:本次调用传入的实参
调用过程(入栈): 返回过程(出栈):
┌─────────────┐ ┌─────────────┐
│ Factorial(1) │ → 栈顶 │ return 1 │ → 弹出
├─────────────┤ ├─────────────┤
│ Factorial(2) │ │ return 2*1 │ → 弹出
├─────────────┤ ├─────────────┤
│ Factorial(3) │ │ return 3*2 │ → 弹出
├─────────────┤ ├─────────────┤
│ Factorial(4) │ → 栈底 │ return 4*6 │ → 弹出
└─────────────┘ └─────────────┘
最终结果:24

递归的效率问题#

问题原因示例
栈溢出递归深度过大Fibonacci(10000)
重复计算子问题被反复求解Fibonacci 树形递归

⚠️ 易错:递归算法的空间复杂度不是调用总次数,而是递归深度(同时存在的最大栈帧数)。Fibonacci(n) 的调用总次数是指数级,但递归深度只有 O(n),所以空间复杂度是 O(n)。

递归转非递归#

方式适用场景方法
用栈模拟无法用简单迭代替代的递归(树遍历)显式栈保存”待处理状态”
直接迭代尾递归或有明确迭代模式(Fibonacci)循环替代

⚠️ 易错:不是所有递归都能简单地用循环替代。树的遍历、图的 DFS 等递归,转非递归时必须用显式栈模拟调用过程。


考研高频考点#

  • ⭐ 括号匹配为什么用栈(LIFO 与嵌套结构的对应关系)
  • ⭐ 三种匹配失败情况的判断(区分检测时机)
  • ⭐ 给定中缀表达式手算转换为后缀表达式
  • ⭐ 给定后缀表达式手算求值过程及结果
  • ⭐ 中缀转后缀过程中运算符栈的变化
  • ⭐ 操作数弹出顺序(左/右操作数)— 除法/减法的易错点
  • ⭐ 递归调用时栈帧中保存的内容(返回地址/局部变量/参数)
  • ⭐ 递归算法的空间复杂度 = 递归深度
  • 前缀表达式的求值方法(从右到左扫描)
  • Fibonacci 递归的重复计算问题
  • 递归转非递归的两种方式

关联页面#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

栈和队列的典型应用
https://lingluoa.icu/posts/stack-applications/
作者
lingluoa
发布于
2026-07-05
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lingluoa
Hello, I'm lingluoa.
公告
欢迎来到我的博客!不定期更新中。
文章目录
标签
站点统计
文章
53
分类
9
标签
75
总字数
175,771
运行时长
0
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.15.6
文章许可
CC BY-NC-SA 4.0