逆波兰式(后缀表达式)|“朝闻道”知识分享大赛
后缀表达式(也称为逆波兰表达式)是一种数学表达式的表示方式,其中操作符放在其相关操作数的后面。这种表示法消除了括号的需求,并通过操作符的位置直接反映了它们的优先级。
数学表达式中我门常以中缀表达式来表示比如式子:(1+2)*3*(4+5*6)
其对应的后缀表达式为:1 2 + 3 * 4 5 6 * + *
后缀表达式的优点:
-
无需括号: 后缀表达式不需要括号来明确运算符的优先级,因为运算符的位置直接反映了它们的优先级。这简化了表达式的书写和理解。
-
易于计算: 后缀表达式可以直接通过从左到右扫描表达式,并使用栈来存储操作数,从而方便地进行计算。这种计算方式避免了中缀表达式中需要考虑运算符优先级的复杂情况。
-
减少运算符优先级判断: 在后缀表达式中,运算符的优先级通过其在表达式中的位置隐含地表示,而不需要显式判断。这使得计算和理解表达式更为直观和简单。
-
减少括号的使用: 由于后缀表达式不需要括号,它在某些情况下可以减少表达式的长度和复杂性。这使得表达式更加紧凑和易读。
-
没有二义性: 后缀表达式没有二义性,每个运算符都有清晰的操作数,不会存在由于括号导致的歧义。这有助于减少错误的发生。
-
更适合计算机处理: 后缀表达式对于计算机来说更自然,因为计算机处理的方式更接近于按顺序执行操作。
总体而言,后缀表达式在计算和计算机科学中有一些实际的优点,尤其是在实现计算器、编译器和计算表达式的应用中。然而,在人类编写和理解表达式时,通常更习惯于使用中缀表达式。
中缀表达式转后缀表达式:
首先我们需要一个栈来存储运算符(operatorStack)和一个容器来存储后缀表达式的结果(postfixExpression)
其次遵循以下四条规则:
- 遇到操作数直接加入后缀表达式
- 遇到左括号直接入栈
- 遇到右括号,将栈中的运算符弹出并加入后缀表达式,直到遇到左括号(弹出左括号)
- 遇到运算符,此运算符优先级高于栈顶的要入栈(或者栈空,栈顶为左括号时)否则从栈中弹出并加入后缀表达式所有优先级高于或等于该运算符的运算符直到遇到括号或者栈空,再将本身运算符插入栈

cin>>origin;//读入表达式
string num="";//储存数字
int flag=0;//统计左括号个数
vector<string>postfixExpression;//后缀表达式
stack<char>operatorStack;//运算符栈
for(auto it:origin){
if(isdigit(it)) num.push_back(it);//如果是数字,则加入数字字符串内
else{//不是数字
if(!num.empty()){//判断数字字符串是否为空
postfixExpression.push_back(num);//数字直接加入后缀表达式
num="";//数字字符串清空
}
if(it=='('){//如果是左括号
flag++;//左括号数量+1
operatorStack.push(it);//左括号直接进栈
}
else if(it==')'){//如果是右括号
if(flag){//先判断是否右左括号(没有左括号的话flag的值为0)
while(!operatorStack.empty()&&operatorStack.top()!='('){//将栈内运算符一直弹出直到栈为空或者遇到左括号
string temp="";
temp.push_back(operatorStack.top());
postfixExpression.push_back(temp);//栈顶运算符加入后缀表达式
operatorStack.pop();//运算符出栈
}
operatorStack.pop();//左括号出栈
flag--;//左括号数量-1
}
else Error();//没有左括号则说明表达式有误
}
else{
if(it=='+'||it=='-'||it=='*'||it=='/'){//如果是运算符
//如果栈为空,或者栈顶是左括号,或者此运算符优先级大于栈顶运算符的优先级直接加入运算符栈
if(operatorStack.empty()||operatorStack.top()=='('||!(Preced(it,operatorStack.top()))) operatorStack.push(it);
else{//否则从栈中弹出并加入后缀表达式所有优先级高于或等于该运算符的运算符直到遇到括号或者栈空,再将本身运算符插入栈
while(!operatorStack.empty()&&Preced(it,operatorStack.top())){
string temp="";
temp.push_back(operatorStack.top());
postfixExpression.push_back(temp);//栈顶运算符加入后缀表达式
operatorStack.pop();//运算符出栈
}
operatorStack.push(it);//本身运算符插入栈
}
}
else Error();//既不是数字也不是运算符和括号,说明表达式有其他字符
}
}
}
//处理完读入的表达式后,左括号的数量应该为0,如果左括号有剩余说明没有相应的右括号即表达式有误
if(flag) Error();
//如果数字字符串不为空则直接进入后缀表达式
if(!num.empty()){
postfixExpression.push_back(num);//数字直接加入后缀表达式
num="";//数字字符串清空
}
//处理运算符栈内剩余的运算符
while(!operatorStack.empty()){
string temp="";
temp.push_back(operatorStack.top());
postfixExpression.push_back(temp);//栈顶运算符加入后缀表达式
operatorStack.pop();//运算符出栈
}
//最后后缀表达式的结果为postfixExpression
for(auto it:postfixExpression) cout<<it<<" ";
cout<<endl;
后缀表达式的计算:
现在我们得到了一个后缀表达式(postfixExpression),我们该怎么进行计算呢
首先我们需要一个数字栈(number)用于临时储存操作数
其次遵循以下两条规则:
- 遇到数字,则把数字插入数字栈内
- 遇到运算符(op),则从数字栈内弹出两个数字分别代操作数2(op2)和操作数(op1)(按照弹出的顺序),然后计算表达式 op1 op op2,再将运算结果插入数字栈内

stack<int>number;//数字栈
vector<string>postfixExpression;//后缀表达式
for(auto it:postfixExpression){
if(isNumber(it))//如果是数字则直接加入数字栈内
number.push(stoi(it));//it是字符串需要转化为数字,可以直接使用string头文件内的stoi函数
else{//如果是运算符
if(number.size()<2) Error();//如果数字栈内元素数量少于2,说明没有足够的操作数,则说明表达式有误
int op2=number.top();number.pop();
int op1=number.top();number.pop();
number.push(calculate(op1,op2,it.front()));//将运算结果插入栈内
}
}
int ans=number.top();//最后数字栈内剩余的一个数字就是表达式的结果
number.pop();
if(!number.empty()) Error();//答案出栈后,栈应该为空,所以栈如果不为空说明表达式有误
cout<<ans<<endl;//输出最后的结果
完整代码:
#include<iostream>
#include<map>
#include<string>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
#define endl "\n"
map<char,int>op;
string origin;
void init(){//方便计算运算符的优先级,我就直接以一个映射关系给运算符赋了一个值
op['+']=1,op['-']=1,op['*']=2,op['/']=2,op['(']=3;
}
bool Preced(char a,char b){//比较运算符优先级
return op[a]<=op[b];
}
bool isNumber(string s){//判断这个字符串是不是数字
for(auto it:s){
if(!isdigit(it)){
return false;
}
}
return true;
}
int calculate(int num1,int num2,char ch){//计算表达式
switch(ch){
case '+':{
return num1+num2;
break;
}
case '-':{
return num1-num2;
break;
}
case '*':{
return num1*num2;
break;
}
case '/':{
if(num2==0){
cout<<"被除数不能为0"<<endl;
exit(1);
}
return num1/num2;
break;
}
default:{
cout<<"表达式有误"<<endl;
exit(1);
}
}
}
void Error(){//报错
cout<<origin<<endl;
cout<<"表达式有误"<<endl;
exit(1);
}
int main(){
init();//初始化映射
//中缀表达式转后缀表达式
cin>>origin;//读入表达式
stack<char>operatorStack;//运算符栈
vector<string>postfixExpression;//后缀表达式
string num="";//储存数字
int flag=0;//统计左括号个数
for(auto it:origin){
if(isdigit(it)) num.push_back(it);//如果是数字,则加入数字字符串内
else{//不是数字
if(!num.empty()){//判断数字字符串是否为空
postfixExpression.push_back(num);//数字直接加入后缀表达式
num="";//数字字符串清空
}
if(it=='('){//如果是左括号
flag++;//左括号数量+1
operatorStack.push(it);//左括号直接进栈
}
else if(it==')'){//如果是右括号
if(flag){//先判断是否右左括号(没有左括号的话flag的值为0)
while(!operatorStack.empty()&&operatorStack.top()!='('){//将栈内运算符一直弹出直到栈为空或者遇到左括号
string temp="";
temp.push_back(operatorStack.top());
postfixExpression.push_back(temp);//栈顶运算符加入后缀表达式
operatorStack.pop();//运算符出栈
}
operatorStack.pop();//左括号出栈
flag--;//左括号数量-1
}
else Error();//没有左括号则说明表达式有误
}
else{
if(it=='+'||it=='-'||it=='*'||it=='/'){//如果是运算符
//如果栈为空,或者栈顶是左括号,或者此运算符优先级大于栈顶运算符的优先级直接加入运算符栈
if(operatorStack.empty()||operatorStack.top()=='('||!(Preced(it,operatorStack.top()))) operatorStack.push(it);
else{//否则从栈中弹出并加入后缀表达式所有优先级高于或等于该运算符的运算符直到遇到括号或者栈空,再将本身运算符插入栈
while(!operatorStack.empty()&&Preced(it,operatorStack.top())){
string temp="";
temp.push_back(operatorStack.top());
postfixExpression.push_back(temp);//栈顶运算符加入后缀表达式
operatorStack.pop();//运算符出栈
}
operatorStack.push(it);//本身运算符插入栈
}
}
else Error();//既不是数字也不是运算符和括号,说明表达式有其他字符
}
}
}
//处理完读入的表达式后,左括号的数量应该为0,如果左括号有剩余说明没有相应的右括号即表达式有误
if(flag) Error();
//如果数字字符串不为空则直接进入后缀表达式
if(!num.empty()){
postfixExpression.push_back(num);//数字直接加入后缀表达式
num="";//数字字符串清空
}
//处理运算符栈内剩余的运算符
while(!operatorStack.empty()){
string temp="";
temp.push_back(operatorStack.top());
postfixExpression.push_back(temp);//栈顶运算符加入后缀表达式
operatorStack.pop();//运算符出栈
}
//最后后缀表达式的结果为postfixExpression
for(auto it:postfixExpression) cout<<it<<" ";
cout<<endl;
//计算后缀表达式的结果
stack<int>number;//数字栈
for(auto it:postfixExpression){
if(isNumber(it))//如果是数字则直接加入数字栈内
number.push(stoi(it));//it是字符串需要转化为数字,可以直接使用string头文件内的stoi函数
else{//如果是运算符
if(number.size()<2) Error();//如果数字栈内元素数量少于2,说明没有足够的操作数,则说明表达式有误
int op2=number.top();number.pop();
int op1=number.top();number.pop();
number.push(calculate(op1,op2,it.front()));//将运算结果插入栈内
}
}
int ans=number.top();//最后数字栈内剩余的一个数字就是表达式的结果
number.pop();
if(!number.empty()) Error();//答案出栈后,栈应该为空,所以栈如果不为空说明表达式有误
cout<<ans<<endl;//输出最后的结果
}
特别的,我们也可以在中缀表达式转后缀表达式的时候直接进行运算
完整代码如下:
#include<iostream>
#include<map>
#include<string>
#include<stack>
#include<algorithm>
using namespace std;
#define endl "\n"
map<char,int>op;
string origin;
void init(){
op['+']=1,op['-']=1,op['*']=2,op['/']=2,op['(']=3;
}
bool Preced(char a,char b){
return op[a]<=op[b];
}
int calculate(int num1,int num2,char ch){
switch(ch){
case '+':{
return num1+num2;
break;
}
case '-':{
return num1-num2;
break;
}
case '*':{
return num1*num2;
break;
}
case '/':{
if(num2==0){
cout<<"被除数不能为0"<<endl;
exit(1);
}
return num1/num2;
break;
}
default:{
cout<<"表达式有误"<<endl;
exit(1);
}
}
}
void Error(){
cout<<origin<<endl;
cout<<"表达式有误"<<endl;
exit(1);
}
int main(){
init();
cin>>origin;
string num="";
int flag=0;//统计左括号个数
stack<int>number;
stack<char>operatorStack;
for(auto it:origin){
if(isdigit(it)) num.push_back(it);
else{
if(!num.empty()){
number.push(stoi(num));
num="";
}
if(it=='('){
flag++;
operatorStack.push(it);
}
else if(it==')'){
if(flag){
while(!operatorStack.empty()&&operatorStack.top()!='('){
if(number.size()<2) Error();
else{
int op2=number.top();
number.pop();
int op1=number.top();
number.pop();
number.push(calculate(op1,op2,operatorStack.top()));
}
operatorStack.pop();
}
operatorStack.pop();
flag--;
}
else Error();
}
else{
if(it=='+'||it=='-'||it=='*'||it=='/'){
if(operatorStack.empty()||operatorStack.top()=='('||!(Preced(it,operatorStack.top()))) operatorStack.push(it);
else{
while(!operatorStack.empty()&&Preced(it,operatorStack.top())){
if(number.size()<2) Error();
else{
int op2=number.top();
number.pop();
int op1=number.top();
number.pop();
number.push(calculate(op1,op2,operatorStack.top()));
}
operatorStack.pop();
}
operatorStack.push(it);
}
}
else Error();
}
}
}
if(flag) Error();
if(!num.empty()){
number.push(stoi(num));
num="";
}
while(!operatorStack.empty()){
if(number.size()<2) Error();
else{
int op2=number.top();
number.pop();
int op1=number.top();
number.pop();
number.push(calculate(op1,op2,operatorStack.top()));
}
operatorStack.pop();
}
int res=number.top();
number.pop();
if(!number.empty()) Error();
cout<<res<<endl;
return 0;
}
相关推荐
【工业自动化】欧姆龙PLC产品线与编程软件体系详解:基于西门子工程师视角的选型指导与避坑指南
内容概要:本文详细解析了欧姆龙PLC的产品线、编程软件体系、通信架构及选型策略,重点从西门子工程师视角出发,对比两者的定位差异与技术路径。文章明确指出欧姆龙存在两套不兼容的系统:传统的C系列(CP/CJ/CS)使用CX-Programmer软件和绝对地址编程,适用于单机设备与老线改造;新型Sysmac平台(NJ/NX/NY)采用Sysmac Studio集成开发环境,支持IEC 61131-3标签变量、EtherCAT运动控制和多领域一体化控制,代表当前技术高地。同时梳理了FINS、CompoWay/F、EtherCAT、EtherNet/IP等多层通信协议的区别,并列出从西门子转型易踩的八大误区,提供清晰的选型决策路径。; 适合人群:具备一定工业自动化背景,熟悉西门子PLC体系,正在转向或需要维护欧姆龙系统的工程师,尤其是从事设备选型、系统集成与产线升级的研发和技术人员。; 使用场景及目标:①帮助西门子背景工程师快速建立对欧姆龙双平台的认知框架;②指导正确选用CP/CJ/NJ/NX系列及配套软件;③避免在通信接口、地址体系、软件版本等方面出现典型错误;④实现老旧系统向新平台平滑迁移。; 阅读建议:此资源强调“先框架后细节”,建议读者先掌握三大平台分流和软件对应关系,再深入具体型号参数。特别注意2026年10月新版Sysmac Studio与CX-One对CJ3M系列的双向支持这一关键更新,在实际项目中需严格核对控制器固件与软件版本匹配性,防止因版本不兼容导致无法连接。
地级市到杭州北京深圳的球面距离.-最新出炉zip
一、资源特点 全新力作:今年精心打造,人工细细打磨,品质上乘。 权威精准:数据来源,准确靠谱,值得信赖。 品质无忧:绝无数据造假,品质有保障,放心使用。 二、适用人群 学生:在校专科生、本科生、研究生,助力学业各个环节。 教研人员:大学教师和学术科研工作者,为教学科研添力。 三、适用专业 社科经济:经济学、社会学专业适用。 规划管理:地理学、城市规划,以及商业、工商管理专业皆可使用。 [特别说明]相关资源数据已全部上传至网盘。 由于纯手工分享粘贴链接和提取码,不排除有极少提取码或者链接出现差错(或内容与标题对不上),若遇到这种情况,不要着急,请私信博主,将会第一时间更正补链接!
openeuler配置
openeuler系统环境配置管理
产业园区运营负责人如何提升园区内科技成果引进与落地的匹配效率?.docx
科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。
CISP-CSE注册云安全工程师备考资源包
CISP-CSE注册云安全工程师备考资源包


