郁闷的C小加(二)
- 描述
-
聪明的你帮助C小加解决了中缀表达式到后缀表达式的转换(详情请参考“郁闷的C小加(一)”),C小加很高兴。但C小加是个爱思考的人,他又想通过这种方法计算一个表达式的值。即先把表达式转换为后缀表达式,再求值。这时又要考虑操作数是小数和多位数的情况。
- 输入
- 第一行输入一个整数T,共有T组测试数据(T<10)。
每组测试数据只有一行,是一个长度不超过1000的字符串,表示这个运算式,每个运算式都是以“=”结束。这个表达式里只包含+-*/与小括号这几种符号。其中小括号可以嵌套使用。数据保证输入的操作数中不会出现负数并且小于1000000。
数据保证除数不会为0。
- 输出
- 对于每组测试数据输出结果包括两行,先输出转换后的后缀表达式,再输出计算结果,结果保留两位小数。两组测试数据之间用一个空行隔开。
- 样例输入
-
21+2=
-
(19+21)*3-4/5=
- 样例输出
-
12+=
-
3.00
-
-
1921+3*45/-=
-
119.20
本题用到的技巧:
1.#include<stack> 其中第一好了栈以及其操作,简化了代码
2.atof函数
#include <stdlib.h> #include <stdio.h> int main() { float f; char *str = "12345.67"; f = atof(str); printf("string = %s float = %f\n", str, f); return 0; }
3.sscanf函数 提取一个字符串中的各个类型的数据,分类提取很方便
WA了数十次,一开始我以为是情况没考虑完,停了几天后忽然想到是不是后缀表达式煤球正确
然后找了 中缀表达式转后缀的方法:
1)规则
中缀表达式a + b*c + (d * e + f) * g,其转换成后缀表达式则为a b c * + d e * f
转换过程需要用到栈,具体过程如下:
1)如果遇到操作数,我们就直接将其输出。
2)如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。
3)如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。
4)如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) "的情况下我们才弹出" ( ",其他情况我们都不会弹出" ( "。也就是说这种操作," + "的优先级最低," ( "优先级最高。
5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
2)实例
规则很多,还是用实例比较容易说清楚整个过程。以上面的转换为例,输入为a + b * c + (d * e + f)*g,处理过程如下:
1)首先读到a,直接输出。
2)读到“+”,将其放入到栈中。
3)读到b,直接输出。
此时栈和输出的情况如下:
4)读到“*”,因为栈顶元素"+"优先级比" * " 低,所以将" * "直接压入栈中。
5)读到c,直接输出。
此时栈和输出情况如下:
6)读到" + ",因为栈顶元素" * "的优先级比它高,所以弹出" * "并输出, 同理,栈中下一个元素" + "优先级与读到的操作符" + "一样,所以也要弹出并输出。然后再将读到的" + "压入栈中。
此时栈和输出情况如下:
7)下一个读到的为"(",它优先级最高,所以直接放入到栈中。
8)读到d,将其直接输出。
此时栈和输出情况如下:
9)读到" * ",由于只有遇到" ) "的时候左括号"("才会弹出,所以" * "直接压入栈中。
10)读到e,直接输出。
此时栈和输出情况如下:
11)读到" + ",弹出" * "并输出,然后将"+"压入栈中。
12)读到f,直接输出。
此时栈和输出情况:
13)接下来读到“)”,则直接将栈中元素弹出并输出直到遇到"("为止。这里右括号前只有一个操作符"+"被弹出并输出。
14)读到" * ",压入栈中。读到g,直接输出。
15)此时输入数据已经读到末尾,栈中还有两个操作符“*”和" + ",直接弹出并输出。
至此整个转换过程完成。程序实现代码后续再补充了。
3)转换的另一种方法
1)先按照运算符的优先级对中缀表达式加括号,变成( ( a+(b*c) ) + ( ((d*e)+f) *g ) )
2)将运算符移到括号的后面,变成((a(bc)*)+(((de)*f)+g)*)+
3)去掉括号,得到abc*+de*f+g*+
计算就好说了,根据后缀表达式,遇到运算符则弹出两个操作数,计算后压栈,最后栈顶元素为计算结果
代码:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <iomanip> #include <stack> using namespace std; int In(char e) { if(e=='(' || e==')' || e=='+' || e=='-' || e=='*' || e=='/') return 1; else return 0; } int cmp(char c) { switch(c) { case '+': case '-': return 1; case '*': case '/': return 2; default : return 0; } } double calculate(char op,double k1,double k2) { double k; switch (op) { case '+': k = k1+k2; break; case '-': k = k1-k2; break; case '*': k = k1*k2; break; case '/': k = k1/k2; break; } return k; } /* 20 (2.6)-(1.666)= 1+2= (19+21)*3-4/5= */ void change(char str[]) { int t,i,k,m; char e,buf[1002],op,s[1002]; double kk,k1,k2; stack <char> S1; stack <double> S2; k=0; S1.push('='); for(i=0;i<strlen(str)-1;++i) //最后一位是 = { if( In(str[i]) ) { switch(str[i]) { case '(': S1.push(str[i]); break; case ')': //遇到右括号, while(S1.top()!='(')//弹出栈元素输出 直到 (, { s[k++]=S1.top(); k1 = S2.top(); S2.pop(); k2 = S2.top(); S2.pop(); S2.push(calculate(S1.top(),k2,k1)); S1.pop(); } if(S1.top()=='(')//弹出(不输出 { S1.pop(); } break; default : while(cmp(str[i])<=cmp(S1.top())) { s[k++]=S1.top(); k1 = S2.top(); S2.pop(); k2 = S2.top(); S2.pop(); S2.push(calculate(S1.top(),k2,k1)); S1.pop(); } S1.push(str[i]); break; }//switch }//if else{ sscanf(str+i,"%[^=()+*/-]",buf); //操作数 for(m=0;m<strlen(buf);m++) s[k++]=buf[m]; //printf("%s",buf); i += strlen(buf)-1; kk = atof(buf); //printf("k = %lf\n",k); S2.push(kk); } }//for while(!S1.empty()) { if(S1.top()!='(' && S1.top()!='=') { s[k++]=S1.top(); k1 = S2.top(); S2.pop(); k2 = S2.top(); S2.pop(); S2.push(calculate(S1.top(),k2,k1)); } S1.pop(); } for(i=0;i<k;i++) printf("%c",s[i]); printf("=\n"); printf("%.2lf\n",S2.top()); } int main() { int t,i; char e,str[1002],buf[1002],op; double k,k1,k2; scanf("%d",&t); getchar(); while(t--) { gets(str); //puts(str); change(str); if(t>0) printf("\n"); } return 0; } /* 20 1+2= (19+21)*3-4/5= */
注意:中缀表达式转为后缀表达式的方法。。。。。。