中缀表达式求值(“+-*/“及“()“)

link
关于理论部分主要是看大佬的解析,下面的文字描述也按大佬的来

代码部分按照自己的理解,使用map+迭代器等简化了大佬的代码

关于中值表达式求值的思路

对于中缀表达式求值来说,一般最常见的直接解决办法就是利用栈,一个栈用来保存操作数,一个栈用来保存操作符。

为了简便起见,暂时表达式中只考虑简单的+,-,*,/运算,只有圆括号,并且都是整数。

假如有这样一个表达式:((3+52)+3)/5+6/42+3

对于这样一个表达式,如果让你来设计操作数和操作符进栈的出栈的规则,你会怎么设计?

先不看这么复杂的表达式,考虑一下简单点的,还是前面的3+25,那么很显然先进行乘法运算,后进行加法运算,但是由于操作符在操作数中间,所以当一个操作符进操作符栈时,该操作符的两个操作数并没有都进入到操作数栈中,那么如何解决呢?只有在后面一个操作符进操作符栈时,前面的一个操作符所作用的两个操作数才会全部进栈。比如3+25,栈的变化过程为:

操作数栈:3 操作数栈:3 操作数栈:3 2

操作符栈:空 操作符栈:+ 操作符栈:+

注意此时遇到操作符“*”,是不是需要弹出操作数栈中的两个操作数进行运算呢,很显然不是,因为乘法运算法比操作符栈的栈顶运算符优先级高,也就是说当前的操作符在“+”前进行运算,那么还需要将当前操作符压栈,则变成:

操作数栈:3 2 操作数栈:3 2 5

操作符栈:+ * 操作符栈:+ *

此时到了表达式的结尾,既然栈顶的操作符的优先级比栈底的操作符的优先级高,那么可以取操作符栈的栈顶操作符和操作数栈的栈顶两个元素进行计算,则得到2*5=10,(注意从操作数栈先弹出的操作数为右操作数)。此时得到10 ,则应该把10继续压到操作数栈中,继续取操作符栈的栈顶操作符,依次进行下去,则当操作符栈为空时表示计算过程完毕,此时操作数栈中剩下的唯一元素便是整个表达式的值。

再换个例子:2*5+3,这个表达式跟前面表达式的结果虽然相同,但是操作数和操作符入栈和出栈的顺序发生了很大变化:

操作数栈:2 操作数栈:2 操作数栈:2 5

操作符栈:空 操作符栈:* 操作符栈:*

此时遇到“+”,而操作符栈的栈顶操作符为“*”,栈顶操作符优先级更高,表示此时可以取操作符栈顶操作符进行运算,那么栈变成:

操作数栈:10 操作数栈:10 3

操作符栈:空 操作符栈:+

后面的过程跟前面一个例子类似。

如果复杂一点,比如包含有括号,连续的乘除法这些怎么处理呢?道理是一样的,对于左括号直接入栈,碰到右括号,则一直将操作符退栈,直到碰到左括号,则括号中的表达式计算完毕。对于连续的乘除法,跟前面例子中处理过程类似。只需要记住一点:只有当前操作符的优先级高于操作符栈栈顶的操作符的优先级,才入栈,否则弹出操作符以及操作数进行计算直至栈顶操作符的优先级低于当前操作符,然后将当前操作符压栈。当所有的操作符处理完毕(即操作符栈为空时),操作数栈中剩下的唯一一个元素便是最终的表达式的值。而操作符的优先级为:+和-优先级是一样的,和/优先级是一样的,+、-的优先级低于、/的优先级。

不过需要注意的是在求值之前需要对表达式进行预处理,去掉空格、识别 负号(区分“-”是作为减号还是负号),提取操作数等。

对于“-”的区分,主要判别方法为:

1)若前一个字符为‘(',则必定为负号;

2)若前一个字符为’)'或者数字,则必定为减号;

3)若前面一个字符为其他运算符,如*,/,则必定是负号;

3)若前面没有字符,即该字符为表达式的第一个字符,则必定是负号。

也就是说只有一种情况下,”-“是作为减号使用的,就是前一个字符为’)'或者数字的时候。

如果判断出“-”是作为负号使用的,这里我采用“#”来代替“-”,并将其作为一种运算(优先级最高)。比如:-3*2

我采取的做法是将"#“入栈,然后当遇到“*”时,由于栈顶操作符为”#",因此取#,然后取操作数栈的栈顶元素(只取一个)进行运算,然后再把结果压栈。

代码

#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>

std::map<std::string, int> priority = {
        {"+", 1},
        {"-", 1},
        {"*", 2},
        {"/", 2},
        {"#", 3},   //负号
        {"(", 0}
};

void stepCalculate(std::stack<int>& operator_data_s, const std::string & opt) {
    if(opt=="#"){
        int operator_data=operator_data_s.top();
        int pre_result=0-operator_data;
        operator_data_s.pop();
        operator_data_s.push(pre_result);
        std::cout<<"操作符:"<<"-(负号)"<<" "<<"操作数:"<<operator_data<<std::endl;
    }
    else if(opt=="*"||opt=="/"){
        int r_operator_data_=operator_data_s.top();
        operator_data_s.pop();
        int l_operator_data_=operator_data_s.top();
        operator_data_s.pop();
        if(opt=="*")
            operator_data_s.push(l_operator_data_*r_operator_data_);
        else
            operator_data_s.push(l_operator_data_/r_operator_data_);
        std::cout<<"操作符:"<<opt<<" "<<"操作数:"<<l_operator_data_<<" "<<r_operator_data_<<std::endl;
    }
    else if(opt=="+"||opt=="-"){
        int r_operator_data_=operator_data_s.top();
        operator_data_s.pop();
        int l_operator_data_=operator_data_s.top();
        operator_data_s.pop();
        if(opt=="+")
            operator_data_s.push(l_operator_data_+r_operator_data_);
        else
            operator_data_s.push(l_operator_data_-r_operator_data_);
        std::cout<<"操作符:"<<opt<<" "<<"操作数:"<<l_operator_data_<<" "<<r_operator_data_<<std::endl;
    }
}

void deleteSpace(std::string & str){
    str.erase(remove(str.begin(), str.end(), ' '), str.end());
}

int midExprCalculate(std::string & midExpr)
{
    deleteSpace(midExpr);
    std::cout<<"中缀表达式:"<<midExpr<<std::endl;
    std::string operator_data="0123456789";
    std::stack<int> operator_data_s;
    std::stack<std::string> operator_symbol_s;
    std::string::iterator it=midExpr.begin();
    while(it != midExpr.end())
    {
        std::string token(1, *it);
        if(token=="-"){
            if(it==midExpr.begin()) {
                token = "#";
            }else if(*(it - 1) == ')' || isdigit(*(it - 1))) {
                token = "-";
            }else{
                token="#";
            }
        }
        if(token == "+" ||token == "#" || token == "-" || token == "*" || token == "/")
        {
            if(operator_symbol_s.empty())
            {
                operator_symbol_s.push(token);
            }
            else
            {
                int token_priority = priority[token];
                while(!operator_symbol_s.empty() && token_priority <= priority[operator_symbol_s.top()])
                {
                    std::string top_symbol = operator_symbol_s.top();
                    operator_symbol_s.pop();
                    stepCalculate(operator_data_s, top_symbol);
                }
                operator_symbol_s.push(token);
            }
        }
        else if(token == "(")
        {
            operator_symbol_s.push(token);
        }
        else if(token == ")")
        {
            while(!operator_symbol_s.empty() && operator_symbol_s.top() != "(")
            {
                std::string top_symbol = operator_symbol_s.top();
                operator_symbol_s.pop();
                stepCalculate(operator_data_s, top_symbol);
            }
            if(!operator_symbol_s.empty() && operator_symbol_s.top() == "(")
            {
                operator_symbol_s.pop();
            }
        }
        else
        {
            std::string opd;
            while(it != midExpr.end() && operator_data.find(*it) != std::string::npos){
                opd += *it;
                ++it;
            }
            --it;
            operator_data_s.push(std::stoi(opd));
        }
        ++it;
    }
    while(!operator_symbol_s.empty())
    {
        std::string top_symbol = operator_symbol_s.top();
        operator_symbol_s.pop();
        stepCalculate(operator_data_s, top_symbol);
    }
    return operator_data_s.top();
}



int main() {

    std::string midExpr = "(((-11+2)*3-1))";
    std::cout<<midExprCalculate(midExpr)<<std::endl;
    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涛涛ALG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值