点击查看代码
/*
给出一个只包含+,-,x,/的非负整数计算表达式,计算该表达式的值,结果保留2位小数点
表达式不超过200个字符,整数和运算符之间用一个空格分隔,没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出
输入样例1:
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
输出样例1:
12178.21
输入样例2:
1 + 3 * 5 / 4 * 8 / 9 * 6 * 2 / 3 / 7 + 3 * 8 / 2
0
输出样例2:
14.90
*/
/*
解题思路:
1、题目给出的是中缀表达式,计算过程分两个步骤:
(1)把中缀表达式转为后缀表达式
(2)计算后缀表达式
2、把中缀表达式转为后缀表达式步骤:
(1)设立一个操作符栈s,临时存放操作符。设立一个队列q,存放后缀表达式
(2)从左至右扫描中缀表达式,如果遇到操作数,则将操作数放入后缀表达式中(注意操作数可能不止一位,因此需要逐位读取,最后再合并为一个操作数)
(3)如果遇到操作符op,则将它与操作符栈的栈顶操作符进行优先级比较
1)如果op的优先级高于栈顶操作符的优先级,则将op压入操作符栈中
2)如果op的优先级低于等于栈顶操作符的优先级,则将操作符栈的栈顶操作符不断弹出到后缀表达式中,直到op的优先级高于栈顶操作符
(4)重复上述步骤,直到中缀表达式扫描完成。如果扫描结束后操作符栈中还有元素,则将它们依次弹出到后缀表达式中
(5)操作符优先级使用map<string,int>容器存储,"*"和"/"的优先级映射为2,"+"和"-"的优先级映射为1
疑问1:为什么操作符op优先级高于栈顶操作符时要压入操作符栈中?
举例中缀表达式3+2*5,如果先计算加法3+2会出错,必须先计算乘法2*5。从左往右扫描中缀表达式时,加号先进入操作符栈,而乘号优先级高于加号,
为了在后缀表达式中先计算乘法,乘号必须在加号前面,所以在操作符栈中乘号要比加号更接近栈顶,所以优先级高于栈顶操作符的op要压入操作符栈中
疑问2:为什么操作符op优先级等于栈顶操作符时不压入操作符栈中?
距离中缀表达式2/3*4,如果设置操作符op优先级等于栈顶操作符时压入栈中,则算法步骤如下:
1)2进入后缀表达式q,q:2
2)/进入操作符栈s,s:/
3)3进入后缀表达式,q:2 3
4)*与操作符栈的栈顶操作符优先级比较,优先级相同,压入操作符栈,s:/ *
5)4进入后缀表达式,q:2 3 4
6)中缀表达式扫描完成,操作符栈非空,将栈中元素逐个弹出到后缀表达式,q:2 3 4 * /
7)计算后缀表达式:2/(3*4),与原来的中缀表达式不同,结果错误,所以优先级相同的操作符op不能压入操作符栈中
注意:本题输入的表达式都没有括号,如果表达式中出现括号,则要在步骤2的(3)中先判断,如果op是左括号就压入操作符栈中。
如果op是右括号,则把操作符栈中的元素全部弹出到后缀表达式,直到遇到左括号时停止
3、计算后缀表达式
(1)从左到右扫描后缀表达式,如果是操作数,则压入栈s中,如果是操作符,则连续弹出两个操作数(注意:先弹出的是第2个操作数temp2,
后弹出的是第1个操作数temp1,顺序关系是temp1/temp2,而不是temp2/temp1),然后根据操作符的符号进行相应计算(如"+"则进行加法),
计算结果存入栈s中。重复以上操作,直到后缀表达式扫描完成,此时栈中只会存在一个数,它就是最终的计算结果
注意:1、除法可能出现浮点数,所以操作数类型要设置为浮点型
2、使用string型变量存储输入的表达式str,然后扫描str,如果当前位是空格,则用str.erase(str[i])去掉空格,将不含空格的str转换为后缀表达式
*/
#include<iostream>
#include<cstdio>
#include<string>
#include<stack>
#include<queue>
#include<map>
using namespace std;
//存储表达式str[i]的每一位
struct node {
double num; //操作数
char op; //操作符
bool flag; //true表示str[i]是操作数,false表示str[i]是操作符
};
string str; //存储输入的表达式
stack<node> s; //先用于存储操作符的栈,后用于存储计算结果
queue<node> q; //存储后缀表达式的队列
map<char, int> op; //操作符优先级映射,加法/减法映射为1,乘法/除法映射为2
//将中缀表达式转换为后缀表达式
void Change() {
node temp; //存储str[i]
for (int i = 0; i < str.size();) { //扫描表达式str
if (str[i] >= '0' && str[i] <= '9') { //如果str[i]是数字
temp.flag = true; //temp标记为数字
temp.num = str[i] - '0'; //减去ASCII码'0'转换为数字
i++; //检查下一位,判断是否还是数字(操作数可能有多位,如100有3位)
while (i < str.size() && str[i] >= '0' && str[i] <= '9') { //下一位还是数字,则它属于该操作数的低位
temp.num = temp.num * 10 + (str[i] - '0'); //更新这个数字=高位*10+低位
i++; //继续检查下一位,直到超出str边界或遇到操作符
}
q.push(temp); //将这个操作数存入后缀表达式的队列中
}
else { //如果str[i]是操作符
temp.flag = false; //temp标记为操作符
//只要操作符栈的栈顶元素比该操作符优先级高
//就把操作符栈的栈顶元素弹出到后缀表达式的队列中
while (!s.empty() && op[str[i]] <= op[s.top().op]) {
q.push(s.top());
s.pop(); //栈顶元素弹出后要手动删除
}
temp.op = str[i];
s.push(temp); //把str[i]操作符压入操作符栈中
i++; //检查下一位
}
}
//扫描完表达式str后,如果操作符栈中还有操作符,就把它们逐个弹出到后缀表达式队列中
while (!s.empty()) {
q.push(s.top());
s.pop(); //栈顶元素弹出后要手动删除
}
}
//计算后缀表达式
double Cal() {
node cur, temp; //cur存储后缀表达式队首元素,temp存储每轮的计算结果
double temp1, temp2; //存储第1和第2个操作数,注意顺序,如temp1/temp2不能写为temp2/temp1
while (!q.empty()) { //只要后缀表达式队列非空
cur = q.front(); //cur记录队首元素
q.pop(); //手动删除队首元素
if (cur.flag == true) s.push(cur); //如果是操作数,则压入栈中
else { //如果是操作符,则进行计算
temp2 = s.top().num; //先弹出temp2
s.pop(); //元素弹出后要手动删除
temp1 = s.top().num; //再弹出temp1
s.pop(); //元素弹出后要手动删除
temp.flag = true; //temp存储计算结果,flag设为true表示temp是操作数
if (cur.op == '+') temp.num = temp1 + temp2; //cur是加号,则进行加法
else if (cur.op == '-') temp.num = temp1 - temp2; //减法
else if (cur.op == '*') temp.num = temp1 * temp2; //乘法
else temp.num = temp1 / temp2; //除法
s.push(temp); //把计算结果压入栈中
}
}
return s.top().num; //计算结束后栈顶元素就是后缀表达式的值
}
int main() {
op['+'] = op['-'] = 1; //设定操作符的优先级
op['*'] = op['/'] = 2;
//while循环有两个判断条件,先读入str,然后执行循环内的代码,然后再读入str,如果str等于数字0,则结束循环,否则继续执行
while (getline(cin, str), str != "0") {
//去掉输入表达式中的所有空格
for (string::iterator it = str.begin(); it != str.end(); it++) {
if (*it == ' ') str.erase(it);
}
while (!s.empty()) s.pop(); //初始化栈
Change(); //将中缀表达式转换为后缀表达式
printf("%.2f\n", Cal()); //计算后缀表达式并输出计算结果 */
}
/* 书上写的会报错
while (getline(cin, str), str != "0") {
for (string::iterator it = str.end(); it != str.begin(); it--) {
if (*it == ' ') str.erase(it); //去掉输入表达式中的所有空格
}
while (!s.empty()) s.pop(); //初始化栈
Change(); //将中缀表达式转换为后缀表达式
printf("%.2f\n", Cal()); //计算后缀表达式
} */
return 0;
}