逆波兰式(后缀表达式)|“朝闻道”知识分享大赛

ThirtyLi 2023-11-21 17:27:04

后缀表达式(也称为逆波兰表达式)是一种数学表达式的表示方式,其中操作符放在其相关操作数的后面。这种表示法消除了括号的需求,并通过操作符的位置直接反映了它们的优先级。


数学表达式中我门常以中缀表达式来表示比如式子:(1+2)*3*(4+5*6) 

其对应的后缀表达式为:1 2 + 3 * 4 5 6 * + *

后缀表达式的优点:

  1. 无需括号: 后缀表达式不需要括号来明确运算符的优先级,因为运算符的位置直接反映了它们的优先级。这简化了表达式的书写和理解。

  2. 易于计算: 后缀表达式可以直接通过从左到右扫描表达式,并使用栈来存储操作数,从而方便地进行计算。这种计算方式避免了中缀表达式中需要考虑运算符优先级的复杂情况。

  3. 减少运算符优先级判断: 在后缀表达式中,运算符的优先级通过其在表达式中的位置隐含地表示,而不需要显式判断。这使得计算和理解表达式更为直观和简单。

  4. 减少括号的使用: 由于后缀表达式不需要括号,它在某些情况下可以减少表达式的长度和复杂性。这使得表达式更加紧凑和易读。

  5. 没有二义性: 后缀表达式没有二义性,每个运算符都有清晰的操作数,不会存在由于括号导致的歧义。这有助于减少错误的发生。

  6. 更适合计算机处理: 后缀表达式对于计算机来说更自然,因为计算机处理的方式更接近于按顺序执行操作。

总体而言,后缀表达式在计算和计算机科学中有一些实际的优点,尤其是在实现计算器、编译器和计算表达式的应用中。然而,在人类编写和理解表达式时,通常更习惯于使用中缀表达式。

中缀表达式转后缀表达式:

 首先我们需要一个栈来存储运算符operatorStack)和一个容器来存储后缀表达式的结果postfixExpression

其次遵循以下四条规则:

  1. 遇到操作数直接加入后缀表达式
  2. 遇到左括号直接入栈
  3. 遇到右括号,将栈中的运算符弹出并加入后缀表达式,直到遇到左括号(弹出左括号)
  4. 遇到运算符,此运算符优先级高于栈顶的要入栈(或者栈空,栈顶为左括号时)否则从栈中弹出并加入后缀表达式所有优先级高于或等于该运算符的运算符直到遇到括号或者栈空,再将本身运算符插入栈
     

 

    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)用于临时储存操作数

其次遵循以下两条规则:

  1. 遇到数字,则把数字插入数字栈内
  2. 遇到运算符(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;
}

 

...全文
151 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

1,034

社区成员

发帖
与我相关
我的任务
社区描述
中南民族大学CSDN高校俱乐部聚焦校内IT技术爱好者,通过构建系统化的内容和运营体系,旨在将中南民族大学CSDN社区变成校内最大的技术交流沟通平台。
经验分享 高校 湖北省·武汉市
社区管理员
  • c_university_1575
  • WhiteGlint666
  • wzh_scuec
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

欢迎各位加入中南民族大学&&CSDN高校俱乐部社区(官方QQ群:908527260),成为CSDN高校俱乐部的成员具体步骤(必填),填写如下表单,表单链接如下:
人才储备数据库及线上礼品发放表单邀请人吴钟昊:https://ddz.red/CSDN
CSDN高校俱乐部是给大家提供技术分享交流的平台,会不定期的给大家分享CSDN方面的相关比赛以及活动或实习报名链接,希望大家一起努力加油!共同建设中南民族大学良好的技术知识分享社区。

注意:

1.社区成员不得在社区发布违反社会主义核心价值观的言论。

2.社区成员不得在社区内谈及政治敏感话题。

3.该社区为知识分享的平台,可以相互探讨、交流学习经验,尽量不在社区谈论其他无关话题。

试试用AI创作助手写篇文章吧