题目描述
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
输入
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
输出
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。
样例输入
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92 0样例输出
12178.21
此题考查的是栈和队列的运用,可以使用两个栈和一个队列来解决,也可以只使用两个栈。
解题思路共两步:
(1)将中缀表达式转为后缀表达式
这一步需要一个队列来装后缀表达式和一个栈来装符号。先从左到右遍历中缀表达式的每个符号和数字,若是数字就入队;若是符号,则判断其与栈顶符号的优先级,若该符号(是右括号或其)优先级低于或等于栈顶符号,则栈顶元素依次出栈并进入队列,直到当前符号优先级大于栈顶符号,就将当前符号进栈。一直到最终输出后缀表达式。
(2)对后缀表达式求值
这一步只需一个栈来装数字。
1.先初始化一个空栈,开始遍历后缀表达式。
2.如果字符是一个操作数,则令其入栈。
3.如果字符是个运算符,则弹出栈里的两个操作数(一定会有两个数在栈里,因为是后缀表达式),进行运算,再把结果入栈。
4.到后缀表达式末尾,从栈中弹出结果。
理解上面两步后,可以将两步合并,最终只使用2个栈就可一次性求出答案:
1.初始化两个栈,分别是数字栈和符号栈。
2.遍历中缀表达式,如果是数字,则入数字栈。
3.如果是符号,则判断其与符号栈的栈顶符号的优先级。若当前符号(是右括号或其)优先级低于或等于栈顶符号,则栈顶元素出符号栈,并在数字栈弹出两个数进行相应运算,再使结果入数字栈。重复该步骤直至当前符号优先级大于栈顶符号,则将当前符号入符号栈。
4.当读完中缀表达式后,则将符号栈里的符号依次出栈,从数字栈弹出两个数进行相应运算,再把结果入数字栈,直到最后一个符号出栈。把数字栈的数字弹出,即为结果。
符号优先级可以使用map来设置,使每个符号对应一个数值,数值越高说明优先级越高。加减号的优先级相等,乘除号的优先级相等,而加减号优先级低于乘除号。
下面是具体的代码实现:
#include<cstdio>
#include<iostream>
#include<stack>
#include<map>
#include<string>
using namespace std;
/**
易错点:
1、stack的pop()返回值为空,因此要获取栈顶元素并出栈,只能先top()后pop()。
2、当前符号优先级高于栈顶符号时,入栈;
当前符号优先级低于或等于栈顶符号时,弹出栈顶符号计算。
(注意等于的情况是弹出!)
3、for循环最后没写i++时,在代码中尤其注意i++的位置,很容易忘记写i++或写错位置。
4、使用top()就一定要同时判空作为条件。
5、读入一行str,要用getline(cin,str);逗号表达式的值为最后一个逗号后的值。
*/
string str;
map<char,int> op; //设置符号优先级的map
stack<double> num; //数字栈
stack<char> sym; //符号栈
double calculator();
double calculate();
int main(){
op['+']=1; //给符号设置优先级
op['-']=1;
op['*']=2;
op['/']=2;
while(getline(cin,str),str!="0"){
for(string::iterator it = str.begin();it!=str.end();it++){
if(*it==' '){
str.erase(it); //把表达式中的空格去掉
}
}
//开始计算当前表达式的值
double result = calculator();
printf("%.2f\n",result);
}
}
double calculator(){
for(int i=0;i<str.length();){
if(str[i]>='0' && str[i]<='9'){ //若当前符号是数字
int temp = str[i++]-'0';
while(i<str.length()&&str[i]>='0'&&str[i]<='9'){
temp = temp*10+(str[i]-'0'); //获取操作数
i++;
}
num.push(temp); //操作数入栈
}else{ //当前符号是操作符
if(sym.empty() || op[str[i]]>op[sym.top()]){ //符号栈为空或当前符号优先级大于栈顶符号,则当前符号直接入栈
sym.push(str[i]);
}else{ //当前符号优先级小于等于栈顶符号,则一直将栈顶符号出栈计算,直至当前符号优先级大于栈顶符号
while(sym.size()!=0 && op[str[i]] <= op[sym.top()]){
//弹出栈顶符号和两个数字并计算
calculate();
}
sym.push(str[i]); //当前符号入符号栈
}
i++;
}
}
//表达式读完了,所有数字、符号都入栈了,开始计算剩下的
while(sym.size()){ //符号栈还有符号
//弹出符号和两个数字并计算
calculate();
}
return num.top(); //数字栈顶元素就是最终结果
}
double calculate(){
char top_op = sym.top(); //栈顶符号出栈
sym.pop();
double num1 = num.top(); //数字栈弹出两个数字
num.pop();
double num2 = num.top();
num.pop();
double result;
switch(top_op){
case '+':
result = num1+num2;
break;
case '-':
result = num2-num1;
break;
case '*':
result = num1*num2;
break;
case '/':
result = num2/num1;
break;
}
num.push(result); //计算结果入数字栈
}
中途遇到过“答案错误50”的情况,原因是每一组输出没换行(代码中已加上)。
最后是我敲代码过程中的一些易错点:
1、stack的pop()返回值为空,因此要获取栈顶元素并出栈,只能先top()后pop()。
2、当前符号优先级高于栈顶符号时,入栈;
当前符号优先级低于或等于栈顶符号时,弹出栈顶符号计算。
(注意等于的情况是弹出!)
3、for循环最后没写i++时,在代码中尤其注意i++的位置,很容易忘记写i++或写错位置。
4、使用top()就一定要同时判空作为条件。
5、读入一行str,要用getline(cin,str);逗号表达式的值为最后一个逗号后的值。