【数据结构】栈与队列 Part2:栈的应用之表达式转换
这里主要记录:中缀表达式与后缀表达式的转换。
中缀表达式,即就是常用于人类理解的一般表达式。
后缀表达式,即就是常用于机器读取的特殊表达式。
此外还有前缀表达式。
给出中缀表达式的例子:9 + ( 3 - 1 ) * 3 + 10 / 2
给出后缀表达式的例子:9 3 1 - 3 * + 10 2 / +
给出前缀表达式的例子:+ + * - 3 1 3 9 / 2 10
实际上这三者给出的是同一个计算式。
关于各种表达式的运算规则,可参阅 前缀、中缀、后缀表达式(逆波兰表达式),这篇文章同时也给出了三种表达式的转换方法。
下面给出一个前缀表达式的计算代码,使用了递归方式运算。
注:1.atof函数是在头文件#include<stdlib.h>中将char*转化为浮点数的函数。
2.static声明是指在程序运行过程中只声明一次,之后遇到该语句时跳过。
1 #include <stdio.h> 2 #include <stdlib.h> 3 double cal() 4 { 5 static char buffer[64]; 6 scanf("%s", buffer); 7 switch(buffer[0]) 8 { 9 case '+': 10 return cal() + cal(); 11 case '-': 12 return cal() - cal(); 13 case '*': 14 return cal() * cal(); 15 case '/': 16 return cal() / cal(); 17 default: 18 return atof(buffer); 19 } 20 } 21 int main() 22 { 23 printf("%lf" "\n", cal()); 24 return 0; 25 }
下面给出利用栈结构将中缀表达式转化为后缀表达式的思路,这也参阅了上文提到的文章。
转化为前缀表达式只需将后缀表达式的输出结果反序即可。
1.创建栈s1,保存运算符;创建栈s2,保存中间结果。
2.读取中缀表达式的顺序是由左至右。
3.若输入为数据,压入s2。
4.若输入为运算符:
1)若s1为空或s1栈顶元素为'(',直接入栈。
2)若s1栈顶元素的优先级比输入的运算符优先级更低或二者相同,直接入栈。(注:优先级与一般相同,即'+'='-'<'*'='/')
3)若s1栈顶元素的优先级比输入的运算符优先级更高,将s1栈顶元素放入s2,重复执行4.。
5.若输入为括号:
1)左括号直接压入s1。
2)右括号则一直弹出到栈顶元素为左括号为止,然后将两个括号销毁。
6.不断重复至输入结束,将s1剩余的运算符放入s2.
7.依次弹出s2中的结果逆序输出后就可以得到后缀表达式了。
对思路进行程序实现:
1 #include<iostream> 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<algorithm> 5 #include<map> 6 #include<stack> 7 #include<queue> 8 9 using namespace std; 10 11 int level(string buffer){ 12 switch (buffer[0]) { 13 case '+': return 1; 14 case '-': return 1; 15 case '*': return 2; 16 case '/': return 2; 17 default: return 0; 18 } 19 } 20 21 int main(){ 22 stack<string> s1; 23 stack<string> s2; 24 string buffer; 25 char a; 26 // scanf("%s",&buffer[0]); 27 cin>>buffer; 28 a=getchar(); 29 while (1){ 30 if (buffer[0]>='0'&&buffer[0]<='9'){ 31 s2.push(buffer); 32 } 33 if (buffer[0]=='+'|| 34 buffer[0]=='-'|| 35 buffer[0]=='*'|| 36 buffer[0]=='/'){ 37 if (s1.empty()==1|| 38 s1.top()=="("){ 39 s1.push(buffer); 40 } 41 else{ 42 if (level(s1.top())<=level(buffer)){ 43 s1.push(buffer); 44 } 45 else{ 46 while (s1.empty()!=1&& 47 s1.top()!="("&& 48 level(s1.top())>level(buffer)){ 49 s2.push(s1.top()); 50 s1.pop(); 51 } 52 s1.push(buffer); 53 } 54 } 55 } 56 if (buffer[0]=='('|| 57 buffer[0]==')'){ 58 if (buffer[0]=='('){ 59 s1.push(buffer); 60 } 61 if (buffer[0]==')'){ 62 while (s1.empty()!=1&& 63 // s1.top()[0]!='('){ 64 s1.top()!="("){ 65 s2.push(s1.top()); 66 s1.pop(); 67 } 68 s1.pop(); 69 } 70 } 71 if (a=='\n'){break;} 72 // scanf("%s",&buffer[0]); 73 cin>>buffer; 74 a=getchar(); 75 } 76 while (s1.empty()!=1){ 77 s2.push(s1.top()); 78 s1.pop(); 79 } 80 while (s2.empty()!=1){ 81 s1.push(s2.top()); 82 s2.pop(); 83 } 84 while (s1.empty()!=1){ 85 printf("%s",s1.top().c_str()); 86 printf(" "); 87 s1.pop(); 88 } 89 return 0; 90 }
需要注意的有三点:
1.用注释内的部分scanf输入时,必须同时使用s1.top()[0]!='('作为结束循环的条件,否则会出现问题,原因尚不明确。
2.用printf输出string类型时,要记为string.c_str(),否则无法使用。
3.这种输入方式无法兼容负数。
(4.果然还是cin和cout好用多了)
P.S.
ios::sync_with_stdio(false);
由于scanf速度比cin快很多,常常在对时间要求很高的时候不得不使用。
但是个人认为cin比scanf好用的多,不容易出bug,因此咨询大佬后发现,只要在主函数的第一句加入上面这一行代码,即可提高cin的速度。
(好像是取消同步之类的)但是也具有副作用,即使用了这个代码后就不可以使用scanf函数输入,否则会出现输入顺序错乱的情况。