Lecture 9:栈在括号匹配中的应用
408 学习笔记:Lecture 9:栈在括号匹配中的应用。保留原有知识内容、公式与图示;有对应原件时提供查看和下载入口。
本页目录
9.1 括号匹配问题
9.1.1 基本概念
定义:#括号匹配
描述: 括号匹配是判断一串括号中的左括号和右括号是否能够按类型、顺序一一对应。
常见括号类型
- 小括号: $$ (\quad ) $$
- 中括号: $$ [\quad ] $$
- 大括号: $$ {\quad } $$
为什么用栈
- 后出现的左括号要先被匹配。
- 这正好符合栈的后进先出规则: $$ \text{LIFO} $$
- 因此可以用栈保存“还没有被匹配的左括号”。
图示解读
- 每遇到一个左括号就入栈;每遇到一个右括号,就与最近尚未匹配的左括号比较。
- 栈顶保存“最内层、最晚出现”的未匹配左括号,因此嵌套括号恰好符合 LIFO。
- 扫描中出现栈空、括号类型不一致,或扫描结束后栈仍非空,均表示匹配失败。
9.2 括号匹配的核心规则
9.2.1 扫描左括号
- 当输入字符是左括号时,将其压入栈中。
- 左括号包括: $$ (\quad [\quad { $$
含义
- 当前左括号还没有遇到对应的右括号。
- 暂时把它存入栈,等待后续匹配。
9.2.2 扫描右括号
- 当输入字符是右括号时,需要让栈顶的左括号出栈。
- 右括号包括: $$ )\quad ]\quad } $$
匹配关系
| 当前右括号 | 栈顶必须是 |
|---|---|
) |
( |
] |
[ |
} |
{ |
原因
- 当前右括号只能匹配离它最近、还未匹配的左括号。
- 这个“最近的未匹配左括号”就在栈顶。
9.2.3 扫描结束
- 处理完所有输入字符后,如果栈为空,说明所有左括号都已匹配。
- 如果栈非空,说明仍然有左括号没有被匹配。
扫描结束后的判定: $$ \text{栈空}\Rightarrow \text{匹配成功} $$
$$ \text{栈非空}\Rightarrow \text{匹配失败} $$
9.3 三种匹配失败情况
9.3.1 右括号多余
情况
- 扫描到右括号时,栈已经为空。
原因
- 没有可用的左括号与当前右括号匹配。
例子
())
扫描到最后一个 ) 时,栈中已经没有 (。
9.3.2 左右括号类型不匹配
情况
- 扫描到右括号时,栈非空。
- 但栈顶左括号和当前右括号不是同一类。
例子
([)]
当扫描到 ) 时,栈顶是 [,但 ) 需要匹配 (。
9.3.3 左括号多余
情况
- 所有字符都扫描完以后,栈仍然非空。
原因
- 栈中剩下的左括号没有对应的右括号。
例子
(()
或者:
((()
扫描结束后仍有未匹配的左括号。
9.4 算法实现
9.4.1 栈结构
原 PDF 提醒:考试中可以直接使用已经定义好的接口名,也可以在代码旁边写出中文含义注释。
本节接口形参中的 &S、&x 是 C++ 引用,不是 C 函数形参;MaxSize=10 只是课件的固定容量示例。完整算法假定同一时刻未匹配左括号数不超过 MaxSize,若要处理任意长度输入,应使用可扩容栈。容量不足与括号不匹配是两种不同原因,不能混为一谈。
栈结构定义
#define MaxSize 10
typedef struct {
char data[MaxSize]; // 静态数组存放栈中元素
int top; // 栈顶指针
} SqStack;
需要使用的接口
void InitStack(SqStack &S);
bool StackEmpty(SqStack S);
bool Push(SqStack &S, char x);
bool Pop(SqStack &S, char &x);
接口含义
InitStack(S):初始化栈。StackEmpty(S):判断栈是否为空。Push(S, x):把字符x入栈。Pop(S, x):栈顶元素出栈,并用x返回。
9.4.2 完整算法
bool bracketCheck(char str[], int length) {
SqStack S;
InitStack(S);
for (int i = 0; i < length; i++) {
if (str[i] == '(' || str[i] == '[' || str[i] == '{') {
if (!Push(S, str[i]))
return false; // 固定栈容量不足;不代表括号本身不匹配
} else if (str[i] == ')' || str[i] == ']' || str[i] == '}') {
if (StackEmpty(S))
return false;
char topElem;
Pop(S, topElem);
if (str[i] == ')' && topElem != '(')
return false;
if (str[i] == ']' && topElem != '[')
return false;
if (str[i] == '}' && topElem != '{')
return false;
}
}
return StackEmpty(S);
}
上述版本允许输入中包含普通字符:普通字符会被跳过。若题目已保证输入只含六种括号,也可以把
else if简写为else。
说明
- 扫描左括号:
Push(S, str[i]);
- 扫描右括号时,如果栈空:
return false;
- 扫描右括号时,如果类型不匹配:
return false;
- 扫描结束后:
return StackEmpty(S);
9.4.3 PDF 代码中的写法修正
原 PDF 中有些地方是课件简写或 OCR 识别结果,正式写题时建议修正为规范形式。
| 位置 | 应写成 | 原因 |
|---|---|---|
for(int i=0,i<length;i++) |
for (int i = 0; i < length; i++) |
for 的三段表达式用分号隔开 |
topElem = Pop(S); |
Pop(S, topElem); |
前文接口是用引用参数返回出栈元素 |
topElem != '[]' |
topElem != '[' |
栈中保存的是单个字符 |
9.5 题型整理
题型:#括号匹配判断
第一部分:解题方法
- 从左到右扫描字符串。
- 遇到左括号就入栈。
- 遇到右括号先看栈是否为空。
- 若栈空,说明右括号多余,匹配失败。
- 若栈非空,弹出栈顶左括号并检查类型。
- 类型不匹配,匹配失败。
- 全部扫描完成后,检查栈是否为空。
- 栈空则匹配成功,栈非空则匹配失败。
第二部分:典型例题
例题 1:判断 ([]{}) 是否匹配
扫描过程:
| 字符 | 操作 | 栈内未匹配左括号 |
|---|---|---|
( |
入栈 | ( |
[ |
入栈 | ([ |
] |
匹配 [,出栈 |
( |
{ |
入栈 | ({ |
} |
匹配 {,出栈 |
( |
) |
匹配 (,出栈 |
空 |
结果: $$ \text{匹配成功} $$
例题 2:判断 ([)] 是否匹配
扫描过程:
| 字符 | 操作 | 结果 |
|---|---|---|
( |
入栈 | 栈中有 ( |
[ |
入栈 | 栈顶为 [ |
) |
要匹配 ( |
但栈顶是 [ |
结果: $$ \text{匹配失败} $$
第三部分:知识点复盘
- 括号匹配问题的关键不是统计数量,而是判断顺序和类型。
- 左右括号数量相等,不一定匹配成功。
- 例如:
([)]
- 数量相等,但顺序错误,所以匹配失败。
- 栈的作用是保存“最近的未匹配左括号”。
相关知识
- 科目导航:数据结构导航
- 阅读顺序:上一篇:队列基本概念 · 下一篇:栈在表达式求值中的应用
- 关联专题:栈与队列代码
9.6 本节复盘
必须掌握
- 括号匹配用栈实现。
- 左括号入栈。
- 右括号触发栈顶左括号出栈。
- 当前右括号必须和栈顶左括号类型匹配。
- 扫描结束后栈必须为空。
易错点
- 扫描到右括号时栈空,直接匹配失败。
- 扫描结束后栈非空,也匹配失败。
GetTop只读不删;括号匹配这里通常需要Pop,因为左括号已经被当前右括号消耗。[]不是一个字符,栈中保存的是'['或']'这样的单个字符。
一句话总结
- 用栈保存未匹配的左括号;每遇到一个右括号,就拿栈顶左括号来配对,最后栈空才算成功。
