codeup 1918 简易计算器
题目描述
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
输入
测试输入包含若干测试用例,每个测试用例占一行,每行不超过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.1 将中缀表达式转换为后缀表达式:**设立一个操作符栈,用于存放操作符,一个操作数队列,用于存放生成的后缀表达式
1.2 这里数字优先级最高其次式 ‘*’ , ‘/’,再其次是加减,括号的优先级最低(这个优先级和平时我们说的先算小括号里面的不是一个概念,这里实际上还是先算小括号里面的),从左到右扫描中缀表达式,如果是数字,就放到队列中(优先级最高),如果是操作符,就要保证这个操作符放到栈中的时候,它的优先级大于原栈顶元素的优先级,也就是说,假如是乘号,原栈顶元素是加号,直接放入栈中就行了;如果是加号,而原栈顶元素是乘号,就要弹出原栈顶,放到队列中,就这样一直弹,弹到栈顶元素比我们要放的元素优先级低就好了。当我们把中缀表达式扫描完后,如果栈中还有操作符,我们就依次弹到队列里。栈空时,一个完整的后缀表达式就形成了。
1.3 如果有括号了也很好处理,遇到左括号,直接放到栈中就行,由于左括号默认优先级最低,所以你之后再处理其他操作符,它仍会正常处理,遇到右括号后,不用放到栈中,直接开始弹栈顶元素,直到遇到左括号(左括号不放到队列中,相当于一个标记)
2计算后缀表达式
遇到数字,放到栈中(与个数无关,照着后缀表达式放就完事了),遇到操作符,就弹出来两个数进行计算,(这里要注意:弹出的第一个数,其实是第二个操作数,弹出的第2个数,其实是第一个操作数,比如:符号是/号,弹出第一个数是2,第二个人弹得是1,实际上计算式是 1/2),计算结果放入栈中,继续扫描后缀表达式,直到最终栈中只剩了一个数,结果就出来了
ps:写这种题得耐心,还得勤动手,可以自己画画图,模拟实现一下过程,这样会更清楚
#include <iostream>
#include <stdio.h>
#include <stack>
#include <queue>
#include <map>
using namespace std;
struct node{
double num;
char op;
bool flag;//true表示操作数,false表示操作符
};
string str;
stack< node > s;
queue< node > q;
map< char , int> op;
void change(string str)
{
for (int i = 0; i <str.length() ;) {
if(str[i]>='0' && str[i] <='9')//如果是数字
{
struct node temp;
temp.flag = true;
temp.num = str[i] - '0';
i++;
while (i <str.length()&&str[i]>='0' && str[i] <='9' )
{
temp.num = temp.num * 10 + (str[i] - '0');
i++;
}
q.push(temp);
}
else if(str[i]=='(' || str[i]==')')
{
if(str[i] == '(')
{
struct node x;
x.op = str[i];
x.flag = false;
s.push(x);
}else
{
while (s.top().op!='(')
{
q.push(s.top());
s.pop();
}
s.pop();//在栈中去除左括号
}
i++;
}else
{
struct node temp;
temp.flag = false;
temp.op = str[i];
while(!s.empty()&&op[temp.op] <=op[s.top().op])
{
q.push(s.top());
s.pop();
}
s.push(temp);
i++;
}
}
//在遍历完中缀表达式后,把栈中的操作符一次弹出到后缀表达式中
while(!s.empty())
{
q.push(s.top());
s.pop();
}
}
double cal()
{
node cur;
node temp;
double temp1,temp2;
while(!q.empty())
{
cur = q.front();
q.pop();
if(cur.flag == true)
{
s.push(cur);
}else
{
temp2 = s.top().num ;
s.pop();
temp1 = s.top().num;
s.pop();
temp.flag = true;
if(cur.op =='+')
{
temp.num = temp1 + temp2;
}else if(cur.op =='-')
{
temp.num = temp1 - temp2;
}else if(cur.op =='*')
{
temp.num = temp1 * temp2;
}else if(cur.op =='/')
{
temp.num = temp1 / temp2;
}
s.push(temp);
}
}
return s.top().num;
}
int main(void)
{
// 这里设置为1和2其实是有门道的,因为‘(’默认为0了,优先级最低
op['+'] = op['-'] = 1;
op['*'] = op['/'] = 2;
while(getline(cin,str),str!="0")//由于cin读取字符串时,遇到空格就会停止,所以用了getline,可以读一行
{
for(string::iterator it =str.end(); it!=str.begin();it--)
{
if(*it == ' ')
{
str.erase(it);//把表达式中的空格全部去掉
}
}
while(!s.empty())
{
s.pop();//初始化
}
change(str);//转为后缀表达式
printf("%.2f",cal());
}
}