栈的应用之中缀转后缀详解
先理一下大概的思路:
中缀表达式一般有操作数,运算符,括号,运算符和括号又存在运算优先级问题,这就是我们需要处理的问题。
中缀表达式转后缀表达式时:
1、遇到操作数或 ‘.’ 直接写入后缀表达式;
2、遇到左括号直接进入操作符的栈中;
3、遇到运算符时,将该运算符与3、栈顶元素比较,如果优先级高于栈顶元素,直接入栈,反之则将栈顶元素出栈,并写入后缀表达式,该运算符入栈;
4、 遇到右括号时,将栈中元素出栈并写入后缀表达式,直到遇到左括号结束,并将左括号也出栈;
5、最后将栈中元素全部出栈并写入后缀表达式。
下面来看具体步骤和代码:
一、中缀转后缀
1、定义一个运算符判断函数,如果该字符是运算符,返回1;否则返回0
int is_operation(char op){ //op是要判断的字符
switch(op){
case '+':
case '-':
case '*':
case '/':return 1;
default:return 0;
}
}
2.定义一个整型函数,用来返回运算符的优先级大小(不是运算符(比如 ‘#’ )的符号优先级为-1 )
int priority(char op){
switch(op){
case '#':return -1;
case '(':return 0;
case '+':
case '-':return 1;
case '*':
case '/':return 2;
default:return -1;
}
}
3.开始转化:这里定义了一个opst[]数组来作为操作符栈。
在中缀表达式的末尾加上一个 ‘#’ 来作为循环结束的条件
while(e[i] != '#'){}
在循环体内:
1)遇到数字和小数点直接写入后缀表达式
if((e[i] >= '0' && e[i] <= '9') || e[i] == '.'){
f[j++] = e[i]; //f[]是存放后缀表达式的数组
//e[]是存放中缀表达式的数组
}
2)遇到左括号直接进操作符栈
else if(e[i] == '('){
opst[top] = e[i]; top++; //opst[]是操作符栈
}
3)遇到右括号把左括号后的操作符全部写入后缀表达式
else if(e[i] == ')'){
t = top - 1; //用 t 变量来表示栈顶下面的那个值(找到'(')
while(opst[t] != '('){ //直到遇到'('才结束循环
f[j++] = opst[--top]; //写入后缀表达式并出栈
t = top - 1;
}
top -- ; //将'('出栈
}
4)比较运算符优先级大小
else if(is_operation(e[i])){
f[j++] = ' ';//用空格分开两个操作数
while(priority(e[i]) <= priority(opst[top-1])){
f[j++] = opst[--top];//优先级小的出栈
}
opst[top] = e[i]; top++;//当前元素进栈
}
5)把剩余的操作符出栈并写入后缀表达式
while(top) f[j++] = opst[--top];
此时已经把中缀表达式转成了后缀表达式,然后我们需要先把操作数从字符转为数字,再进行运算。
二、后缀表达式的计算
1.操作数:字符转为浮点型数字(整数部分和小数部分)。
这里的i一定要用指针,因为要在该函数里改变i的值并使之在调用的地方有效。
double readNumber(char f[],int *i){}
1)整数部分
字符转浮点型,这里有两种方法:
第一种是直接用 f[ i ] - '0’ ,第二种是使用atof函数:atof(f[i]).
这里的x每加一次都要*10,这是因为十进制的进制是10.
while(f[*i] >= '0' && f[*i] <= '9'){ //直到这个整数全部读出了就结束(遇到其他符号)。
x = x*10 + (f[*i] - '0'); //还原操作数
(*i)++; //向后寻找
}
2)小数部分
这里与整数部分类似,就是多了个标志变量k,k每加1,说明该数的小数部分就多了一位,在最后需要除以相应的位数。
if(f[*i] == '.'){
(*i)++; //后移一位(移到'.'的后面一位)
while(f[*i] >= '0' && f[*i] <= '9'){
x = x*10 + (f[*i] - '0');
(*i)++;
k++; //小数部分多一位,k+1
}
}
while(k!=0){ //每次循环除以10,直到k为0
x = x/10.0;
k -- ;
}
2.计算后缀表达式的值
定义两个浮点型变量x1,x2 来表示两个操作数, obst[]表示操作数栈
1)循环,直到遇到’#'结束
while(f[i] != '#'){}
2)将操作数入栈
if(f[i] >= '0' && f[i] <= '9'){
obst[top] = readNumber(f,&i);
top++;
}
3)如果是空格(即后缀表达式里的操作数分隔符),就继续向下
else if(f[i] == ' ') i++;
4)进行四则运算
else if(f[i] == '+'){
x1 = obst[--top];
x2 = obst[--top];
obst[top] = x1 + x2; //将运算结果存入操作数栈,继续进行运算
top++; i++;
}
else if(f[i] == '-'){
x1 = obst[--top];
x2 = obst[--top];
obst[top] = x1 - x2;
top++; i++;
}
else if(f[i] == '*'){
x1 = obst[--top];
x2 = obst[--top];
obst[top] = x1 * x2;
top++; i++;
}
else if(f[i] == '/'){
x1 = obst[--top];
x2 = obst[--top];
obst[top] = x2 / x1;
top++; i++;
}
5)返回操作数栈的第一个数字,即为最后的结果。
return obst[0]; //后缀表达式运算的最后结果
到这里,中缀转后缀表达式,以及后缀表达式的运算都搞定啦。
希望能帮到对中缀转后缀还是不太理解的同学。
最后在此附上程序完整代码. GitHub地址:https://github.com/jie12366/postfix.git