认识前缀、中缀、后缀表达式:
一般我们平时用的计算式都是中缀表达式,因为符号都是在操作数的中间的。相对应的符号在操作数后面的就叫后缀表达式(也称逆波兰式),符号在操作数前面的就叫前缀表达式(也称波兰式)
为什么要把中缀表达式转化为后缀,前缀?
计算机没法计算带有括号,以及区分优先级的表达式,或者说很难计算。使用后缀,前缀,消除了括号和优先级。
计算机如何计算后缀,前缀表达式?
计算后缀:从左到右遍历后缀表达式,遇到操作数,放进栈,遇到操作符,栈顶两个数出栈,进行运算,运算结果放进栈,直到读完后缀表达式。计算前缀:从左到右遍历前缀表达式,遇到操作符,放进栈,遇到操作数,查看栈顶,栈顶为操作符,放进栈,栈顶为操作数,取出栈顶操作数和操作符,进行运算,运算后继续判断栈顶的情况。
中缀表达式转换成前缀表达式
(1)构建两个栈,一个存运算符一个存操作数。运算符(以括号分界点)在栈内遵循越往栈顶优先级不降低的原则进行排列。(2)从右至左扫描中缀表达式,从右边第一个字符开始判断:
- 如果当前字符是数字,则分配到数字串的结尾并将数字串直接输出。
- 如果是运算符,则比较优先级。如果当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),则将运算符直接入栈; 否则将栈顶运算符出栈并输出,直到当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),再将当前运算符入栈。
- 如果是括号,则根据括号的方向进行处理。如果是右括号,则直接入栈;
- 否则,遇右括号前将所有的运算符全部出栈并输出,遇到右括号后将左右的两括号一起删除。
(3) 重复上述操作(2)直至扫描结束,将栈内剩余运算符全部出栈并输出,再逆缀输出字符串。中缀表达式也就转换为前缀表达式了。
中缀表达式转换成后缀表达式:
和中缀表达式转换成前缀表达式相似
思想:
首先要知道中缀表达式如何转换成后缀(前缀)表达式,中缀表达式是人容易理解的表达式,而对计算机来说,计算中缀表达式是很困难的。所以我们先把中缀表达式转化成后缀表达式或者转换成前缀表达式后在计算。
中缀表达式是区分,由于不同的运算符间存在优先级,同一优先级的运算间又存在着运算结合问题。所以简单地从左往右的计算是不可以的。而前缀和后缀表达式是不区分符号优先级的,且它们两个没有括号运算符。后缀表达式是从前面往后面读,而前缀表达式的区别在于它是从后面往前面读的。所
以我们一般是把中缀表达式转换成后缀表达式来计算的。
这里我们用后缀表达式来讲解。表达式转换一般都是用到堆栈(简称“栈”),可以用顺序栈或链式栈,这里用顺序栈进行讲解,表达式转换的过程中,遇到运算数就直接输出,所以运算数的顺序是不会改变的,改变的只是运算符的顺序,所以表达式转换是要用到符号栈来保存运算符的。
应用栈将中缀表达式转换成后缀表达式的基本过程为:从头到尾读取中缀表达式的每个元素,运算规则分为下列5种:
(1)遇到运算数直接输出。
(2)遇到左括号直接将其压栈。
(3)若遇到是的右括号,表明括号内的中缀表达式已经扫描完毕,将栈顶的运算符依次出栈,直至遇到左括号,然后把左括号也弹出,但不输出(右括号是不入栈的)。
(4)遇到运算符,则与栈顶符号运算级对比,若当前运算符的运算级大于栈顶运算符的运算级,则入栈,而小于或等于都要先出栈,再把当前的运算符压栈。
(5)若中缀表达式计算完成,则将栈中剩余的运算符全部出栈。
在上述处理的过程的一个关键是不同运算符优先级的设置,在程序实现中,可以用一个数字来代表运算符的优先级,优先级越高,数值越大,在这里我是把左括号的优先级设为1,加号和减号设为2,乘号和除号设为3,右括号设为4(右括号可以不设置优先级)。
而转换成后缀表达式后,计算就比较简单了。求值要用到一个数据栈来保存运算数,而在前面的遇到运算数就不该直接输出了,而是遇到运算数就进数据栈,每当从符号栈出来一个符号,就要从数据栈中出两个数据,并且进行运算。运算完成后再将结果压入数据栈,直至表达式结束,而结束后,最后的计算结果就会栈数据栈中。
C语言版代码:
#include<stdio.h> #include<stdlib.h> /*数据栈*/ struct shuju { int data[100]; int top; }; /*符号栈*/ struct fuhao { char symbol[100]; int top; }; void InitOperateNum(struct shuju *StackNum) { StackNum->top = -1; } void InitOperateSymbol(struct fuhao *StackSymbol) { StackSymbol->top = -1; } /*存入数据栈*/ void Inshuju(struct shuju *StackNum, int num) { StackNum->top ++; StackNum->data[StackNum->top] = num; } /*存入符号栈*/ void Infuhao(struct fuhao *StackSymbol, char ch) { StackSymbol->top ++; StackSymbol->symbol[StackSymbol->top] = ch; } /*读取数据栈*/ int Randshuju(struct shuju *StackNum) { return StackNum->data[StackNum->top]; } /*读取符号栈*/ char Randfuhao(struct fuhao *StackSymbol) { return StackSymbol->symbol[StackSymbol->top]; } /*从数据栈取出数据*/ int Putshuju(struct shuju *StackNum) { int x; x = StackNum->data[StackNum->top]; StackNum->top --; return x; } /*从符号栈取出符号*/ char Putfuhao(struct fuhao *StackSymbol) { char c; c = StackSymbol->symbol[StackSymbol->top]; StackSymbol->top --; return c; } /*符号优先级判断*/ int judge(char ch) { if(ch == '(') { return 1; } if(ch == '+' || ch == '-') { return 2; } else if(ch == '*' || ch == '/') { return 3; } else if(ch == ')') { return 4; } } /*四则运算*/ int Math(int v1, int v2, char c) { int sum; switch(c) { case '+' : { sum = v1 + v2; break; } case '-' : { sum = v1 - v2; break; } case '*' : { sum = v1 * v2; break; } case '/' : { sum = v1 / v2; break; } } return sum; } int main() { struct shuju data; struct fuhao symbol; InitOperateNum(&data); InitOperateSymbol(&symbol); int i, t, sum, v1, v2; char c; i = t = sum = 0; char v[100] = {0}; char *str = (char *)malloc(sizeof(char)*200); while((c = getchar()) != '\n') { str[i] = c; i ++; } str[i] = '\0'; for(i = 0; str[i] != '\0'; i ++) { if(i == 0 && str[i] == '-') { v[t++] = str[i]; } else if(str[i] == '(' && str[i+1] == '-') { i ++; v[t++] = str[i++]; while(str[i] >= '0' && str[i] <= '9') { v[t] = str[i]; t ++; i ++; } Inshuju(&data, atoi(v)); while(t > 0) { v[t] = 0; t --; } if(str[i] != ')') { i --; Infuhao(&symbol, '('); } } else if(str[i] >= '0' && str[i] <= '9') { while(str[i] >= '0' && str[i] <= '9') { v[t] = str[i]; t ++; i ++; } Inshuju(&data, atoi(v)); while(t > 0) { v[t] = 0; t --; } i --; } else { if(symbol.top == -1) { //如果符号栈没有元素,直接把符号放入符号栈 Infuhao(&symbol, str[i]); } else if(judge(str[i]) == 1) { //如果此符号是'(',直接放入符号栈 Infuhao(&symbol, str[i]); } else if(judge(str[i]) == 2) { //如果此符号是'+'或'-',判断与栈顶符号是优先级 if(judge(Randfuhao(&symbol)) == 1) { //如果栈顶符号是'(',放入符号栈 Infuhao(&symbol, str[i]); } else if(judge(Randfuhao(&symbol)) == 2) { //如果栈顶符号是'+'或'-',则出栈运算 while(symbol.top >= 0 && data.top >= 1) { //循环出栈 v2 = Putshuju(&data); v1 = Putshuju(&data); sum = Math(v1, v2, Putfuhao(&symbol)); Inshuju(&data, sum); //将运算结果压入数据栈 } Infuhao(&symbol, str[i]); //新符号进栈 } else if(judge(Randfuhao(&symbol)) == 3) { //如果栈顶符号是'*'或'/',则进符号栈 while(symbol.top >= 0 && data.top >= 1) {