中缀表达式转后缀表达式并计算
中缀表达式的计算是很常见的问题,转为后缀表达式之后可以更清晰的进行运算。
题目来源:洛谷P1175
题目描述
8-(3+26)/5+4可以写为:8 3 2 6+5/-4+
其计算步骤为:
8 3 2 6 * + 5 / – 4 +
8 3 12 + 5 / – 4 +
8 15 5 / – 4 +
8 3 – 4 +
5 4 +
9
编写一个程序,完成这个转换,要求输出的每一个数据间都留一个空格。
输入格式
就一行,是一个中缀表达式。输入的符号中只有这些基本符号0123456789±/^(),并且不会出现形如2-3的格式。
表达式中的基本数字也都是一位的,不会出现形如12形式的数字。
所输入的字符串不要判错。
输出格式
若干个后缀表达式,第I+1行比第I行少一个运算符和一个操作数,最后一行只有一个数字,表示运算结果。
输入输出样例
输入
8-(3+2*6)/5+4
输出
8 3 2 6 * + 5 / - 4 +
8 3 12 + 5 / - 4 +
8 15 5 / - 4 +
8 3 - 4 +
5 4 +
9
Code
/*
中缀表达式转后缀表达式
*/
#include <iostream>
#include <cstring>
#include <stack>
#include <cmath>
using namespace std;
string s,ss = "";
int arr[120];
int apoiner = 0;
stack<char> p;
int pri(char ch){ //运算符优先级计算
switch(ch){
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
case '(':
case ')':
return 0;
default: return -1;
}
}
void mid_to_back(string s, string & ss){ //中缀表达式转化为后缀表达式 有修改的参数一定要传引!!!!!!用
for(int i = 0; i < s.size(); i++){
if(s[i] >= '0' && s[i] <= '9'){ //是数字,直接添加到表达式中
ss += s[i];
}
else if(s[i] == '('){ //左括号直接压栈
p.push(s[i]);
}
else if(s[i] == ')'){ //右括号,弹栈 直至到左括号
while(p.top() != '(') { //直到左括号为止,把所有的符号弹出
ss += p.top();
p.pop();
}
p.pop();//弹出左括号,注意左括号不输出
}
else{
while(!p.empty() && pri(s[i]) <= pri(p.top())){
//将比当前运算符优先级高的运算符加到表达式中,并且弹出栈
ss += p.top();
p.pop();
}
p.push(s[i]); //当前运算符压栈
}
}
while(!p.empty()){ //!!最后如果栈中还有剩余的符号 要加出来
ss += p.top();
p.pop();
}
}
int cal(int a, int b, char ch){
switch(ch){
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
case '^': return pow(a,b);
default: return -1;
}
}
void print_res(string ss, int sp){ //从sp位置开始输出表达式
for(int i = sp; i < ss.size() - 1;i++){
cout << ss[i] << " ";
}
cout << ss[ss.size()-1] << endl;
}
void cal_res(string ss){ //计算后缀表达式结果
for(int i = 0 ;i < ss.size(); i++){
if(ss[i] >= '0' && ss[i] <= '9'){
arr[apoiner] = ss[i]-'0'; //数组模拟栈 否则无法获得栈底指针
apoiner ++;
}
else{
apoiner -= 2;
int a = arr[apoiner];
int b = arr[apoiner+1];
//cout << "ab " << a << " " << b << endl;
int res = cal(a,b,ss[i]); //计算
//cout << res << endl;
//cout << apoiner << endl;
arr[apoiner] = res;
apoiner ++;
//cout << arr[0] << endl;
//cout << apoiner << endl;
//输出分两段,当前数组里的数+未处理到的后缀表达式 每次计算过程都要输出一次表达式/中间过程
for(int j = 0; j < apoiner; j++){
cout << arr[j] << " ";
}
if(i+1 != ss.size()) print_res(ss, i+1); //每次计算都会输出结果,最后一次只输出结果的时候就不输出后缀表达式了,否则会多一个符号
}
}
}
int main(){
cin >> s;
mid_to_back(s, ss); //将前缀表达式转化为后缀表达式
print_res(ss,0); //从开始输出结果
cal_res(ss); //计算结果,并在每次中间结果时输出当前过程
printf("\n");//最后一次输出回车
return 0;
}
TIPS
- 需要栈底指针时用数组模拟栈
- 字符串作为参数需要传引用
- 由于要中间结果,每次计算后需要输出
- 不能每次输出字符串,因为中间结果可能出现两位数和负数
- 核心转换:数字直接存,将比当前符号优先级高的符号出栈存起来,当前符号进栈;遇到右括号则将直至左括号的符号都弹出。
- 注意转化后括号不保留
- 若是不需转成后缀表达式,只有计算,则直接在上述核心转换步骤中改为遇到符号将前面比当前符号优先级的符号的运算计算出来。(附上一篇风格很好的代码)