栈和队列的典型应用
1429 字
7 分钟
栈和队列的典型应用
Warning
含AI生成内容
栈和队列的典型应用
括号匹配
为什么用栈
括号的嵌套规则要求最后出现的左括号最先被匹配,与栈的 LIFO 特性完全一致。
算法流程
- 初始化空栈
- 从左到右扫描表达式
- 遇左括号
(、[、{:入栈 - 遇右括号
)、]、}:若栈空则失败(右括号多余),否则弹栈配对检查 - 扫描结束后栈为空则成功,否则失败(左括号多余)
三种失败情况(⭐常考)
| 失败类型 | 示例 | 检测时机 |
|---|---|---|
| 括号不匹配 | {[)} | 弹栈比较时 |
| 右括号多余 | ()) | 遇到右括号时栈已空 |
| 左括号多余 | (() | 扫描结束栈非空 |
复杂度
| 指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(n) — 每个字符扫描一次 |
| 空间复杂度 | O(n) — 最坏全为左括号 |
表达式求值
三种表达式对比
| 类型 | 写法 | 示例(a + b * c) |
|---|---|---|
| 中缀表达式 | 运算符在操作数中间 | a + b * c |
| 前缀表达式(波兰式) | 运算符在操作数前面 | + a * b c |
| 后缀表达式(逆波兰式) | 运算符在操作数后面 | a b c * + |
后缀表达式不需要括号,运算顺序由运算符出现的先后唯一确定。
中缀转后缀
使用运算符栈,按优先级规则决定何时弹出:
- 操作数 → 直接输出
(→ 入栈)→ 弹出栈顶直到(,(弹出不输出- 运算符 → 弹出栈中优先级 ≥ 当前运算符的运算符,再入栈
- 结束 → 弹出栈中剩余运算符
运算符优先级:
| 运算符 | 栈外优先级 | 栈内优先级 |
|---|---|---|
( | 最高 | 最低 |
* / | 高 | 高 |
+ - | 低 | 低 |
) | 最低 | — |
⚠️ 易错:左括号
(栈外优先级最高(保证能入栈),栈内优先级最低(保证不会被普通运算符弹出)。只有)才能弹出它。理解这一点是正确模拟中缀转后缀的关键。
⚠️ 易错:步骤 4 中的 ≥ 不能改成 >。如果改成严格大于,相同优先级的运算符不会被弹出,会破坏左结合性。例如
a - b + c应该先算 a-b,如果 + 不弹出 -,就变成先算 b+c 了。
手算示例:a + b * c - (d / e) → a b c * + d e / -
后缀表达式求值
使用操作数栈:
- 操作数 → 入栈
- 运算符 → 弹出两个操作数,先弹出的是右操作数,后弹出的是左操作数,计算结果压回栈
- 结束 → 栈顶为最终结果
⚠️ 易错:弹出顺序是先右后左。对于减法和除法(不满足交换律),弄反顺序会得到完全不同的结果。例如后缀
5 3 -应计算5 - 3 = 2,弄反就变成3 - 5 = -2。
复杂度
| 操作 | 时间复杂度 |
|---|---|
| 中缀转后缀 | O(n) |
| 后缀求值 | O(n) |
| 空间复杂度 | O(n) |
栈在递归中的应用
递归的两个要素
- 递归表达式(递归体):问题可分解为规模更小的同类子问题
- 边界条件(递归出口):存在不需要递归就能直接求解的最小规模
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 递归的重复计算问题
- 递归转非递归的两种方式
关联页面
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章智能推荐
1
队列(Queue)
数据结构2026-07-05
2
栈(Stack)
数据结构2026-07-05
3
图的遍历
数据结构2026-07-07
4
图的基本概念与存储结构
数据结构2026-07-07
5
串的基本概念
数据结构2026-07-05
随机文章随机推荐










