题目在这里:我是题目
思路
题目给出的是中缀表达式,所以要计算他的值主要是两个步骤:
- 中缀表达式转后缀表达式。
- 计算后缀表达式。
下面分别讲一下这两步:
步骤1:中缀表达式转后缀表达式
- 设立一个操作符栈,用以临时存放操作符;设立一个数组或者队列,用以存放后缀表达式。
- 从左至右扫描中缀表达式。如果碰到操作数(注意:操作数可能不止一位,因此需要一位一位读入然后合并在一起),就把操作数加入后缀表达式中。
- 如果碰到操作符op,就将其优先级与操作符栈的栈顶操作符的优先级比较。
- 若op的优先级高于栈顶操作符的优先级,则压入操作符栈。
- 若op的优先级低于或等于栈顶操作符的优先级,则将操作符栈的操作符不断弹出到后缀表达式中,直到op的优先级高于栈顶操作符的优先级。
- 重复上述操作,直到中缀表达式扫描完毕,之后若操作符栈中仍有元素,则将他们依次弹出至后缀表达式中。
-
所谓操作符的优先级即他们计算的优先级,其中乘法==除法>加法==减法,在具体实现上可以用map建立操作符和优先级的映射,优先级可以用数字表示,例如乘法和除法优先级为1,加法和减法优先级为0。
-
关于为什么当op高于栈顶时就压入操作栈,这里举一个例子:
对中缀表达式3+2x5,显然如果先计算加法3+2会引起错误,必须先计算乘法2x5。当从左到右扫描时,加号先进入操作符栈,而由于乘号优先级大于加号,其必须先计算,因此在后缀表达式中乘号必须在加号前面,于是在栈中乘号要比加号更靠近栈顶,以让其先于加号进入后缀表达式。 -
关于为什么op等于栈顶时不能直接压入操作符栈,这里举一个例子:
对中缀表达式2/3x4,如果设定优先级相等时直接压入操作符栈,那么算法步骤如下:
a)2进入后缀表达式,当前后缀表达式为2。
b)/进入操作符栈,当前操作符为/。
c)3进入后缀表达式,当前后缀表达式23。
d)x与操作符栈的栈顶元素/比较,相等,压入操作符栈,当前操作符栈为/*
e)4进入后缀表达式,当前后缀表达式为234。
f)中缀表达式扫描完毕,操作栈非空,将其全部弹入后缀表达式,最终后缀表达式变为234*/。
g)计算该后缀表达式,发现其变成了2/(3x4),显然这跟原来中缀表达式的计算结果完全不同。 -
本题没有出现括号,但是如果出现括号,处理方法也很简单,只需要在步骤3的a与b之前判断,如果是左括号‘(’,就压入操作符栈;如果是右括号’)’,就把操作符栈里的元素不断弹出到后缀表达式直接碰到左括号’(’。
步骤2:计算后缀表达式
从左到右扫描后缀表达式,如果是操作数,就压入栈;如果是操作符,就连续弹出两个操作数(注意:后弹出的是第一操作数,先弹出的是第二操作数),然后进行操作符的操作,生成的新操作数压入栈中。反复后缀表达式扫描完毕,这时栈中只会存在一个数,就是最终的答案。
- 注意除法可能导致浮点数,因此操作数类型要设成浮点型。
- 题目中说肯定是合法表达式,因此上面操作一定能够成功。但如果题目表明可能出现非法表达式,那就要注意每一步的对象是否合法。
代码
//1918-ProblemA-简单计算器
/*
中缀转后缀,计算后缀表达式
步骤一:中缀表达式转后缀表达式
设立一个操作符栈,用以临时存放操作符,根据要求将符合要求的操作符从栈顶弹出到后缀表达式中;
设立一个数组或队列,用以存放后缀表达式。从左到右扫描中缀表达式,如果遇到操作数,
就把操作数加入后缀表达式中;如果遇到操作符,根据后缀表达式的特性,就将其操作符与操作符栈
的栈顶操作符的优先级比较:如果高于栈顶元素的优先级,则压入操作符栈;若低于或等于,则将操作符栈的栈顶元素不断弹出到后缀表达式中,
直到其优先级大于栈顶元素。重复以上操作,直到中缀表达式扫描完毕,之后将操作符栈剩余的操作符依次弹入到后缀表达式中。
步骤二: 计算后缀表达式
从左到右扫描后缀表达式,如果是操作数,则入栈;如果是操作符,则连续弹出两个操作数,
然后进行操作符的操作生成的新操作数压入栈中,直到后缀表达式扫描完毕,最终答案就是栈中最后一个元素。
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <stack>
#include <map>
#include <string>
using namespace std;
struct node{
double num;//数字
char op;//操作符
bool flag;//区分数字(1)操作符(0)
};
string str;
stack<node> s;//操作符栈
queue<node> q;//后缀表达式队列
map<char,int> opp;//操作符优先级映射
void change(){//中缀表达式转后缀表达式
double num;
node temp;
int len = str.length();
for(int i=0;i<len; ){
if(str[i] >= '0' && str[i] <= '9'){
temp.flag = 1;
temp.num = str[i++] - '0';//读取这个操作数的首位
while(i< len && str[i] >= '0' && str[i] <= '9'){//读取这个操作数
temp.num = temp.num * 10 + (str[i] - '0');
i++;
}
q.push(temp);
}
else{//操作符
temp.flag = 0;
//若低于或等于,则将操作符栈的栈顶元素不断弹出到后缀表达式中,
//直到其优先级大于栈顶元素
while(!s.empty() && opp[str[i]] <= opp[s.top().op]){
q.push(s.top());
s.pop();
}
//如果高于栈顶元素的优先级,则压入操作符栈
temp.op = str[i];
s.push(temp);
i++;
}
}
//如果操作符栈中还有操作符,就把它弹出到后缀表达式队列中
while(!s.empty()){
q.push(s.top());
s.pop();
}
}
//计算后缀表达式
double cal(){
double temp1,temp2;
node cur,temp;
while(!q.empty()){//后缀表达式非空
cur = q.front();//cur暂存队首元素
q.pop();
if(cur.flag == 1) s.push(cur);//操作数入栈
else{//操作符
temp2 = s.top().num;//弹出栈顶第一操作数
s.pop();
temp1 = s.top().num;//弹出第二操作数
s.pop();
temp.flag = 1;//临时记录操作数
if(cur.op == '+') temp.num = temp1 + temp2;//加法
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() {
opp['+'] = opp['-'] = 1;//设定操作符优先级
opp['*'] = opp['/'] = 2;
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("%.2lf\n",cal());//计算后缀表达式的值
}
return 0;
}