括号匹配
代码中的括号通常符合一下特性:
- 括号成对存在
- 左右括号通常类型匹配,大括号匹配大括号
如果存在括号序列(((()))())。从左向右,后进入的左括号先被匹配,体现了LIFO原则,所以可以使用栈来实现。
算法过程:
访问括号序列,如果访问到左括号,只需要进行压入栈中即可。而如果碰到右括号则需要进行判断:
- 查看当前栈是否为空,如果为空,说明没有左括号与这个括号匹配,匹配失败,否则继续。
- 弹出栈顶,如果类型符合,则匹配成功,查看下一个括号。
- 如果类型不符合说明序列有误,匹配失败
重复访问,直到访问完毕所有括号序列,此时再进行查看栈,如果栈不为空,说明有左括号没有匹配到右括号,序列有误。
代码实现:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define MaxSize 10
bool bracketMatch(char * str, int length){
//数组实现栈
char Stack[MaxSize];
int top = -1;
for (int i = 0 ; i < length ;i ++){
//左括号入栈
if (str[i] == '(' || str[i] == '{' || str[i] =='[')
Stack[++top] = str[i];
else if (str[i] == ')' || str[i] == '}' || str[i] ==']'){
//检测到右括号,弹出栈顶进行匹配
if (top == -1)
return false;
char top_char = Stack[top--];
switch (top_char){
case '{':
if (str[i] != '}')
return false;
break;
case '(':
if (str[i] != ')')
return false;
break;
case '[':
if (str[i] != ']')
return false;
}
}else
return false; //含有非括号成分,序列有误
}
return top == -1; //最后查看栈顶,左括号是否完全匹配
}
表达式求值
表达式说明
主要解决的为这类算数表达式:(1+2) * 3 / 4。
表达式通常有三类东西:运算符±,操作数,界符即括号
表达式通常分为三类:
- 前缀表达式,也称为波兰表达式。用处不大。
- 中缀表达式,也就是用的最经常的表达式
- 后缀表达式,也称为逆波兰表达式。常用于计算机。最经常考,考点
前缀中缀后缀指的是运算符在运算值之间的位置,加减乘除都是二目运算符,通常写在两个运算值中间,所以这种称为中缀表达式,其他两种类似。
前缀表达式,后缀表达式的优点:可以不需要界符
表达式转换算法
将中缀表达式转换为前缀后缀表达式遵循两步走:
- 确定各个运算符的运算顺序
- 根据运算顺序按照前缀或者后缀的规则进行写
由于运算符的运算顺序有同级别的存在,所以运算顺序不一定相同,那么转换出来的前缀后缀表达式不一定相同。
如果需要转换出来的表达式相同,需要规定死运算顺序。
- 一般转换成后缀表达式采用“左优先”原则,即运算符如果优先级相同,从左往右优先,如果优先级不同,优先级高的先算。
- 转换为前缀表达式采用“右优先”
这样导致结果还有一种好处,转换为后缀表达式中的操作符从左往右的顺序与定义运算顺序相同,前缀表达式从右往左的顺序与定义运算顺序相同。
将后缀表达式转换为中缀表达式:
从左往右扫描
- 如果为操作数,入栈
- 如果为操作符,取出栈顶两个操作数,先取出来的为右操作数,后取出来的为左操作数
- 按照中缀编写,添加括号,压入栈中
- 继续迭代,直至扫描完毕
如果是表达式求值,将计算结果压入栈中即可。
中缀表达式转换为后缀表达式算法与求值
重点内容
算法过程:按照顺序扫描序列,碰到不同类型进行不同操作:
- 如果为操作数,将操作数压入操作数栈中
- 如果为操作符,弹出操作符栈顶所有优先级大于或者等于该操作符的操作符,弹出的时候进行相应转化,压回操作数栈。然后将操作符压入操作符栈中。
- 如果为界符,左界符直接压入操作符栈中,如果为右界符,弹出操作符栈直至到左界符(一并弹出),并且进行相应计算,界符不加入后缀表达式
- 直至扫描完毕,将操作符栈所有操作符弹出进行相应计算,最后剩余在操作数栈的即为结果
如果需要计算值,压回操作数栈压入计算的值即可,最终剩余的就是表达式的结果。
栈的其他应用以及队列应用
栈的其他应用:递归调用,用于保存现场数据
队列的应用:
- 树的层次遍历
- BFS
- 操作系统中的FCFS
- 以及缓冲区的队列