介绍
中缀表达式,指的是我们日常运算中所用的、将运算符号写在要运算的数字中间、利用括号等进行辅助标明优先级的计算式。如(3+2)*9/(4-1)这种运算式。
而后缀表达式(又称为逆波兰表达式),则是将运算符置于要计算的数字之后的运算式(3 + 2后缀表示就是3 2 +),运算符作用于它前面的两位数字,计算式严格按照从左至右的顺序,不再借用括号表明优先级。相比于中缀表达式,后缀表达式是更方便于计算机求解的一种式子。
如例式可化为3 2 + 9 * 4 1 - /
计算
计算过程为:+作用于3,2,则原式=5 9 * 4 1 - /
*作用于5,9 ,原式=45 4 1 - /
-作用于4 1,原式=45 3 /
/作用于45 3,结果为15,答案与中缀表达式相同
这个计算过程,我们可以用栈来模拟实现
如洛谷P1449
对于字符串s,做以下处理:
当遍历时,遇到.时,将前面的字符串转换为数字压入栈中;
int a=0,b=0;//初始化,用于记录数字的值
for (int i=0; i<s1.size(); i++) {
if (s1[i]>='0'&&s1[i]<='9') {
a=b*10+s1[i]-'0';
b=a;
}
else if(s1[i]=='.') {//说明完整数字结束
s.push(a);//压入栈中
a=0,b=0;
}
遇到运算符号时,记录栈顶,将栈顶弹出,记录,弹出,这两个数字就是运算符作用的数字,再将计算结果压入栈中
else {
int n=s.top();//记录、弹出栈顶
s.pop();
int m=s.top();//同上操作
s.pop();
if (s1[i]=='+') s.push(m+n);//将运算结果压入栈中
else if(s1[i]=='-') s.push(m-n);//注意分辨减数与被减数
else if(s1[i]=='*') s.push(m*n);
else s.push(m/n);//注意分辨除数与被除数
}
最后结果就是栈顶的值
完整代码
#include <bits/stdc++.h>
using namespace std;
int main() {
stack<int> s;
string s1;
cin>>s1;
int a=0,b=0;
for (int i=0; i<s1.size(); i++) {
//压入数字
if (s1[i]>='0'&&s1[i]<='9') {
a=b*10+s1[i]-'0';
b=a;
}
else if(s1[i]=='.') {
s.push(a);
a=0,b=0;
}
else if(s1[i]=='@') break;//判断结束
//进行运算
else {
int n=s.top();
s.pop();
int m=s.top();
s.pop();
if (s1[i]=='+') s.push(m+n);
else if(s1[i]=='-') s.push(m-n);
else if(s1[i]=='*') s.push(m*n);
else s.push(m/n);
}
}
cout<<s.top();
return 0;
}
前缀表达式(又称波兰表达式),指的是将运算符放在数字前、从右到左计算的一种表达式
如3+2 转换前缀则为+ 3 2
运算方法也正好与后缀相反,是从右到左将字符串压入栈中,其余操作参照后缀表达式
表达式的转换
对于中缀表达式转换为后缀(或前缀)表达式一般有这几种方法
手动转换
方式为:
1)将表达式按计算顺序用括号括起来 如例式变为(((3+2)*9)/(4-1))
2)将符号向后移出对应的括号,如(3+2)变为(3 2)+,移完去除括号,就是后缀表达式
前缀表达式则是向前移出对应括号
利用二叉树转换
中缀表达式其实就是二叉树中序遍历的结果,前缀就是同一棵二叉树前序遍历的结果,后缀则为后序遍历结果(前篇已介绍三种遍历方式)
利用栈转换
遇到数字则不入栈,遇运算符,优先级大于栈中符号才入栈中,否则弹出,直到栈内符号优先级小于于它或者为空或遇到括号
遇(无条件入栈,遇)则弹出所有符号直到遇到第一个(
图解模拟过程:
数字入队,优先级大于栈顶入栈
if (s[i]>='0'&&s[i]<='9') q.push_back(s[i]);//数字入队
else if (s[i]=='('||s1.empty()||precedence(s[i])>precedence(s1.top())) s1.push(s[i]);
遇)时,符号出栈直到遇到(
else if(s[i]==')') {
while(!s1.empty()&&s1.top()!='(') {
q.push_back(s1.top());
s1.pop();//弹出所有大于它的运算符入队中
}
s1.pop();//弹出括号
}
/优先级等于*,将*弹出,/入栈
//不满足大于栈顶符号时将栈内符号弹出
while (!s1.empty()&&precedence(s1.top())>=precedence(s[i])&&s1.top()!='('){
q.push_back(s1.top());//弹出的入队
s1.pop();
}
s1.push(s[i]);
遇(将符号弹出
将剩余符号全部弹出
while(!s1.empty()){//将栈内符号都弹出
q.push_back(s1.top());
s1.pop();
}
得到3 2 + 9 * 4 1 - /
栈外元素可以用另一个队接收,方便直接运算
#include <bits/stdc++.h>
using namespace std;
stack<char> s1;
deque<char> q;
int precedence(char op) {
if (op=='+'||op=='-') {
return 1;
} else if (op=='*'||op=='/') {
return 2;
}
return 0;
}
int main() {
string s;
cin>>s;
for (int i=0; i<s.size(); i++) {
if (s[i]>='0'&&s[i]<='9') q.push_back(s[i]);//数字入队
else if (s[i]=='('||s1.empty()||precedence(s[i])>precedence(s1.top())) s1.push(s[i]);
else if(s[i]==')') {
while(!s1.empty()&&s1.top()!='(') {
q.push_back(s1.top());
s1.pop();//弹出所有大于它的运算符入队中
}
s1.pop();//弹出括号
}
else {
//不满足大于栈顶符号时将栈内符号弹出
while (!s1.empty()&&precedence(s1.top())>=precedence(s[i])&&s1.top()!='('){
q.push_back(s1.top());//弹出的入队
s1.pop();
}
s1.push(s[i]);
}
}
while(!s1.empty()){//将栈内符号都弹出
q.push_back(s1.top());
s1.pop();
}
while(!q.empty()){
cout<<q.front()<<" ";
q.pop_front();//输出改变后的式子
}
return 0;
}
中缀转前缀时需要从后往前遍历,当符号优先级大于等于栈顶时入栈,括号的判断需要反着来,其余同上