通常人在书写的时候习惯是书写中缀表达式也叫逆波兰式
,然而在计算机处理的时候中缀表达式的效率远小于后缀表达式,即操作数在前面,运算符在后面例如:
中缀表达式 A+B 后缀表达式AB+
A+BC------- ABC*+
AB+C*D -------- AB*CD*+
D+A/(B_C)-------- DABC-/+
后缀表达式计算时,所有运算按照运算符出现的顺序,严格从左到右,每个操作符取前两个操作数进行运算,运算后的结果仍然作为下次的操作数,这样做与中缀表达式完全等价,即计算次序和运算结果完全相同,并且不再使用括号,逆波兰式的主要特点在于运算对象(操作数)不变,运算符号反应运算顺序,采用逆波兰式很好的表达简单运算符,其优点在于已与计算机处理表达式,因此算术表达式计算问题进行分解,先将中缀转换为后缀表达式在进行计算。
这个过程一共分为两步,第一步,把输入的表达式转换为后缀表达式,第二步,计算后缀表达式
有两种
1.根据树的遍历
2.利用栈
今天主要说的是利用栈的转换
中缀表达式转换为后缀表达式
1.创建栈
2.从左向右顺序获取中缀表达式
a.数字直接输出
b.运算符: 遇到’ ( ‘直接入栈,遇到’ ) ‘将栈中‘ ( ‘之后入栈的全部输出,同时‘ ( ‘出栈但是不输出。其他符号将符号栈中的元素依次出栈并输出,直到遇到比当前符号优先级更低的符号或者’ ( ‘,将当前符号入栈。
3.获取完后,将栈中剩余的符号依次输出
例如:12 * (3 + 4) - 6 + 8 / 2
依次获取:
12 ,是数字,直接输出
后缀表达式:12
符号栈:
’ * ’ ,是运算符,入栈
后缀表达式:12
符号栈:*
’ ( ‘,左括号,直接入栈
后缀表达式:12
符号栈: * (
3 , 数字 ,输出
后缀表达式:12 3
符号栈: * (
‘ + ’,运算符 ,入栈
后缀表达式:12 3
符号栈: * ( +
4 ,数字,输出
后缀表达式:12 3 4
符号栈: * ( +
‘ )’,右括号,栈中元素依次出栈并输出知道遇到左括号,并且左括号也要出栈且不输出
后缀表达式:12 3 4 +
符号栈: *
‘ - ’,操作符,减号的优先级低于乘号所以乘号出栈并输出,此时站内没有符号,减号入栈
后缀表达式:12 3 4 + *
符号栈: -
6 ,数字,输出
后缀表达式:12 3 4 + * 6
符号栈: -
’ + ‘,操作符 ,优先级与减号相同(也就是说没有减号的优先级高)所以减号出栈输出,加号入栈
后缀表达式:12 3 4 + * 6 -
符号栈: +
8 ,数字 ,输出
后缀表达式:12 3 4 + * 6 - 8
符号栈: +
‘ / ’,操作符,比减号的优先级高直接入栈
后缀表达式:12 3 4 + * 6 - 8
符号栈: + /
2 ,数字,输出
后缀表达式:12 3 4 + * 6 - 8 2
符号栈: + /
中缀表达式获取完后,将栈中剩余元素依次出栈输出
后缀表达式:12 3 4 + * 6 - 8 2 / +
符号栈:
以上就是中缀表达式转后缀表达式
转换了之后也要知道后缀表达式是怎么计算的
还是要利用栈
- 依次遍历
- 检测是否是数字,是数字就压栈,是操作符从栈中依次取出当前操作数的右操作数和左操作数与当前操作符进行运算,结果压栈。
遍历结束,运行结果就是栈顶元素。
比如:12 3 4 + * 6 - 8 2 / +
数字入栈
栈:12 3 4
’ + ’ ,遇到运算符从栈中依次取出当前操作数的右操作数和左操作数与当前操作符进行运算,结果压栈 ,3 +4 =7
栈:12 7
’ * ’ , 运算符 ,12 * 7 =84
栈:84
6 ,入栈
栈:84 6
’ - ’ ,运算符 ,84 -6 =78
栈:78
8 2 ,入栈
栈 : 78 8 2
’ / ’ ,运算符 , 8 /2 =4
栈:78 4
’ + ’ ,运算符 78+4 =82
栈:82
最后的结果就是:82
测试可运行的代码如下:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct node { int data; struct node *next; }node, *link; typedef struct snode { char c; struct snode *next; }snode, *slink; void empty(link &s); void empty1(slink &s); void push(link &s, int x); void push1(slink &s, char x); int pop1(slink &s); int pop(link &s); char opTop(slink &s); void calculate(link &s, slink &s1); void dealExpression(); int main(void) { dealExpression(); printf("\n"); system("pause"); return 0; } void empty(link &s) { s = NULL; } void empty1(slink &s) { s = NULL; } void push(link &s, int x) { node *p = (link)malloc(sizeof(node)); p->data = x; p->next = s; s = p; } void push1(slink &s, char x) { snode *p = (slink)malloc(sizeof(snode)); p->c = x; p->next = s; s = p; } int pop(link &s) { int x; if (s == NULL) { printf("it is an empty stack"); } else { x = s->data; s = s->next; return x; } } int pop1(slink &s) { snode *p = s; char x; if (s == NULL) { printf("it is an empty stack"); } else { x = p->c; s = s->next; free(p); return x; } } char opTop(slink &s1) { if (s1 == NULL) { return 0; } else { return s1->c; } } //计算,将栈中的数值和运算符进行计算 void calculate(link &s, slink &s1) { int number1, number2; char opf; number1 = pop(s); number2 = pop(s); opf = pop1(s1); int tmpResult = 0; switch (opf) { case '+': tmpResult = number1 + number2; break; case '-': tmpResult = number2 - number1; break; case '*': tmpResult = number2 * number1; break; case '/': tmpResult = number2 / number1; break; } push(s, tmpResult); } //将中缀表达式转化为后缀,并且存储在数值中 void dealExpression() { char change[100]; int i = 0; char a, b; node *s; snode *s1; //定义节点一定要初始化节点,要不然会发生分配内存错误 empty(s); empty1(s1); printf("please input expression with kuohao:\n"); char currentchar; scanf("%c", ¤tchar); // 转换过程 while (currentchar != '=') { switch (currentchar) { case '+': case '-': if (opTop(s1) == 0) { push1(s1, currentchar); } else { while (opTop(s1) == '+' || opTop(s1) == '-' || opTop(s1) == '*' || opTop(s1) == '/') { a = pop1(s1); change[i] = a; i++; printf("%c", a); } push1(s1, currentchar); } scanf("%c", ¤tchar); break; case '*': case '/': if (opTop(s1) == NULL) { push1(s1, currentchar); } else { while (opTop(s1) == '*' || opTop(s1) == '/') { a = pop1(s1); change[i] = a; i++; printf("%c", a); } push1(s1, currentchar); } scanf("%c", ¤tchar); break; case '(': push1(s1, currentchar); scanf("%c", ¤tchar); break; case ')': while (opTop(s1) != '(') { b = pop1(s1); change[i] = b; i++; printf("%c", b); } pop1(s1); scanf("%c", ¤tchar); break; default: int opNum = 0; while (currentchar >= '0'¤tchar <= '9') { // 通过递归实现二位到以上的数 opNum = opNum * 10 + currentchar - '0'; scanf("%c", ¤tchar); } change[i] = char(opNum); i++; printf("%d", opNum); break; } } while (opTop(s1) != 0) { a = pop1(s1); change[i] = char(a); i++; printf("%c", a); } // 将转化的后缀表达式进行计算,如果遇到操作符,直接计算就可,无需考虑优先级,只需要考虑到从左至右的顺序 int j = 0; while (j<i) { switch (change[j]) { case '+': case '-': case '*': case '/': push1(s1, change[j]); calculate(s, s1); // 重要,因为break语句导致我调试了很久 break; default: push(s, int(change[j])); } j++; } while (opTop(s1) != 0) { calculate(s, s1); } int result; result = pop(s); printf("\n%d", result); }