中缀表达式求值

转自:点击打开链接 海子


中缀表达式求值问题

  中缀表达式的求值问题是一个比较常见的问题之一,我们通常在编写程序时,直接写出表达式让编译器去处理,很少去关心编译器是怎么对表达式进行求值的,今天我们来一起了解一下其中具体的原理和过程。

  表达式一般来说有三种:前缀表达式、中缀表达式、后缀表达式,其中后缀表达式又叫做逆波兰表达式。中缀表达式是最符合人们思维方式的一种表达式,顾名思义,就是操作符在操作数的中间。而前缀表达式和后缀表达式中操作符分别在操作数的前面和操作数的后面。举个例子:

  3+2

  这个是最简单的一个中缀表达式。而其等同的前缀表达式形式为+32,后缀表达式形式为32+。

  那么一些朋友可能会问既然中缀表达式最符合人类的思维习惯,为什么还需要前缀表达式和后缀表达式?先看一个例子,假如在前面的表达式基础上加一点东西:

  3+2*5

  此时的表达式很显然,如果进行计算,则先计算2*5,最后计算加法。但是如果需要先计算加法运算呢?则必须加上括号,(3+2)*5。

  而如果用后缀表达式来表示,则为 32+5*,那么该表达式的计算顺序为3+2 —> (3+2)*5。

  区别就在这里,后缀表达式不需要用括号就能表示出 整个表达式哪部分运算先进行。同理,前缀表达式也是如此。这种表达式正好最符合计算机的处理方式,因为后缀表达式和前缀表达式求值不需要考虑优先级的问题,计算机处理起来便简单很多。

  今天我们这里主要讲解中缀表达式和后缀表达式(前缀表达式和后缀表达式很类似,就不做过多赘述),下面是讲解大纲:

  • 中缀表达式如何直接求值?
  • 后缀表达式如何直接求值?
  • 中缀表达式如何转换为后缀表达式?

1.中缀表达式直接求值

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

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

  假如有这样一个表达式:((3+5*2)+3)/5+6/4*2+3

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

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

  操作数栈: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 <cstdio>
#include <cstring>
#include <stack>
#define LL long long
using namespace std;
#define maxn 5005
char s[maxn];

LL num[maxn];
char sig[maxn];
int nn,sn;

int pri(char c){
    if(c=='+'||c=='-') return 1;
    if(c=='*'||c=='/') return 2;
    if(c=='(') return 0;
}

void cal(){
    if(sig[sn]=='+') num[nn-1]=num[nn]+num[nn-1];
    if(sig[sn]=='-') num[nn-1]=num[nn-1]-num[nn];
    if(sig[sn]=='*') num[nn-1]=num[nn-1]*num[nn];
    if(sig[sn]=='/') num[nn-1]=num[nn-1]/num[nn];
    nn--;
    sn--;
}


LL solve(int l,int r){
    nn=sn=0;
    int curn=0;
    for(int i=l;i<=r;i++){
        if(isdigit(s[i])){
            curn=curn*10+s[i]-'0';
        }
        else {
            if(s[i]=='(') {
                sig[++sn]=s[i];
                continue;
            }
            if(s[i-1]!=')'){
                num[++nn]=curn;
                curn=0;
            }
            if(s[i]==')'){
                while(sig[sn]!='(') cal();
                sn--;
                continue;
            }
            if(sn&&pri(s[i])<=pri(sig[sn])) cal();
            sig[++sn]=s[i];
        }
    }
    if(s[r]!=')') num[++nn]=curn;
    while(nn>1) cal();
    return num[1];
}

int main(){
    while(~scanf("%s",s)){
        cout<<solve(0,strlen(s)-1)<<endl;
    }
    return 0;
}


对于中缀表达式求值,一般需要借助栈来完成。具体的步骤如下: 1. 创建一个运算符栈和一个操作数栈。 2. 从左到右遍历中缀表达式的每一个元素。 - 如果当前元素是操作数,直接将其压入操作数栈。 - 如果当前元素是运算符,分为以下几种情况处理: - 如果运算符栈为空或者栈顶运算符是"(",则直接将该运算符压入运算符栈。 - 如果当前运算符的优先级大于栈顶运算符的优先级,将当前运算符压入运算符栈。 - 如果当前运算符的优先级小于或等于栈顶运算符的优先级,将栈顶运算符弹出,并将其与操作数栈栈顶的两个操作数进行计算,将计算结果压入操作数栈,并将当前运算符压入运算符栈。重复这一步骤直到当前运算符的优先级大于栈顶运算符的优先级。 - 如果当前运算符为")",则依次弹出运算符栈的栈顶元素,直到栈顶元素为"("。将弹出的运算符与操作数栈栈顶的两个操作数进行计算,将计算结果压入操作数栈。 3. 遍历完中缀表达式后,将运算符栈中的剩余运算符依次弹出,并对操作数栈栈顶的两个操作数进行计算,将计算结果压入操作数栈。 4. 遍历完后,将操作数栈中的唯一元素即为中缀表达式的求值结果。 需要注意的是,对于乘法和除法这样的高优先级运算符,要先进行计算再进行压栈操作,而对于加法和减法这样的低优先级运算符,只有在栈顶运算符优先级小于当前运算符时才进行计算。 这就是中缀表达式求值的基本步骤。通过按照以上步骤运算,可以得到中缀表达式c的求值结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值