目录
- 3.3.1栈在括号匹配中的应用
- 流程图
- 算法代码
- 3.3.2栈在表达式求值中的应用
- 1. 中缀表达式 (需要界限符)
- 2. 后缀表达式 (逆波兰表达式)
- 中缀表达式转后缀表达式-手算
- 重点:中缀表达式转后缀表达式-机算
- 重点:后缀表达式的计算—机算
- 3.前缀表达式 (波兰表达式)
- 4.中缀表达式的计算(用栈实现)
- 5. 知识回顾
- 3.3.3栈在递归中的应用
- 调用过程
- 3.3.4 队列的应用
3.3.1栈在括号匹配中的应用
用栈实现括号匹配
-
((())) 最后出现的左括号最先被匹配 (栈的特性—LIFO);
-
遇到左括号就入栈;
-
遇到右括号,就“消耗”一个左括号 (出栈);
匹配失败情况:
-
扫描到右括号且栈空,则该右括号单身;
-
扫描完所有括号后,栈非空,则该左括号单身;
-
左右括号不匹配;
流程图
算法代码
#define MaxSize 10
typedef struct{
char data[MaxSize];
int top;
} SqStack;
//初始化栈
InitStack(SqStack &S)
//判断栈是否为空
bool StackEmpty(SqStack &S)
//新元素入栈
bool Push(SqStack &S, char x)
//栈顶元素出栈,用x返回
bool Pop(SqStack &S, char &x)
bool bracketCheck(char str[], int length){
SqStack S; //声明
InitStack(S); //初始化栈
for(int i=0; i<length; i++){
if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
Push(S, str[i]); //扫描到左括号,入栈
}else{
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;
}
}
StackEmpty(S); //栈空说明匹配成功
}
3.3.2栈在表达式求值中的应用
1. 中缀表达式 (需要界限符)
运算符在两个操作数中间:
① a + b
② a + b - c
③ a + b - c*d
④ ((15 ÷ (7-(1+1)))×3)-(2+(1+1))
⑤ A + B × (C - D) - E ÷ F
2. 后缀表达式 (逆波兰表达式)
运算符在两个操作数后面:
① a b +
② ab+ c - / a bc- +
③ ab+ cd* -
④ 15 7 1 1 + - ÷ 3 × 2 1 1 + + -
⑤ A B C D - × + E F ÷ - (机算结果)
A B C D - × E F ÷ - + (不选择)
中缀表达式转后缀表达式-手算
-
步骤1: 确定中缀表达式中各个运算符的运算顺序
-
步骤2: 选择下一个运算符,按照[左操作数 右操作数 运算符]的方式组合成一个新的操作数
-
步骤3: 如果还有运算符没被处理,继续步骤2
“左优先”原则: 只要左边的运算符能先计算,就优先算左边的 (保证运算顺序唯一);
中缀:A + B - C * D / E + F
① ④ ② ③ ⑤
后缀:A B + C D * E / - F +
重点:中缀表达式转后缀表达式-机算
初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。从左到右处理各个元素,直到末尾。可能遇到三种情况:
- 遇到操作数: 直接加入后缀表达式。
- 遇到界限符: 遇到 ‘(’ 直接入栈; 遇到 ‘)’ 则依次弹出栈内运算符并加入后缀表达式,直到弹出 ‘(’ 为止。注意: ‘(’ 不加入后缀表达式。
- 遇到运算符: 依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到 ‘(’ 或栈空则停止。之后再把当前运算符入栈。
按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。
重点:后缀表达式的计算—机算
先出栈的是“右操作数”
3.前缀表达式 (波兰表达式)
运算符在两个操作数前面:
① + a b
② - +ab c
③ - +ab *cd
中缀表达式转前缀表达式—手算
-
步骤1: 确定中缀表达式中各个运算符的运算顺序
-
步骤2: 选择下一个运算符,按照[运算符 左操作数 右操作数]的方式组合成一个新的操作数
-
步骤3: 如果还有运算符没被处理,就继续执行步骤2
“右优先”原则: 只要右边的运算符能先计算,就优先算右边的;
中缀:A + B * (C - D) - E / F
⑤ ③ ② ④ ①
前缀:+ A - * B - C D / E F
注意: 先出栈的是“左操作数”
4.中缀表达式的计算(用栈实现)
两个算法的结合: 中缀转后缀 + 后缀表达式的求值
初始化两个栈,操作数栈 和运算符栈
若扫描到操作数,压人操作数栈
若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈 (期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈项元素并执行相应运算,运算结果再压回操作数栈)
5. 知识回顾
3.3.3栈在递归中的应用
迷宫求解也用到栈
函数调用的特点:=最后被调用的函数最先执行结束(LIFO)
函数调用时,需要用一个栈存储:
- 调用返回地址(下一条指令的地址)
- 实参(传递的)
- 局部变量(自己的)
递归调用时,函数调用栈称为 “递归工作栈”:
- 每进入一层递归,就将递归调用所需信息压入栈顶;
每退出一层递归,就从栈顶弹出相应信息;
缺点:太多层递归可能回导致栈溢出(空间复杂度升高);也可能含有很多重复计算过程
适合用“递归”算法解决:可以把原始问题转换为属性相同,但规模较小的问题;
调用过程
3.3.4 队列的应用
树的层次遍历、图的广度优先遍历、操作系统FCFS(先来先服务)