用Python实现表达式求值,递归解法和栈解法(附详细代码和测试)

问题描述:

输入四则运算表达式,仅由数字、+、-、*、/、()组成,没有空格,要求求其值,假设运算符结果都是整数,/结果也是整数

输入:
(2+3)*(5+7)+9/3
输出:
63

第一种解法:eval()函数

>>> eval('(3+5)*2')
16

第二种解法:递归

搞清楚表达式的递归定义很关键

  • 表达式=项或项的加减
  • 项=因子或因子的乘除
  • 因子=(表达式)或整数

代码:

def expression_value():
    global i
    a=term_value()
    if i==len(s):
        return a
    c=s[i]
    while c in '+-':
        i+=1
        b=term_value()
        if c=='+':
            a+=b
        else:
            a-=b
        if i==len(s):
            return a
        else:
            c=s[i]
            if c==')':
                i+=1
                return a
    return a
def term_value():
    global i
    a=factor_value()
    if i==len(s):
        return a 
    c=s[i]
    while c in '*/':
        i+=1
        b=term_value()
        if c=='*':
            a*=b
        else:
            a=a//b
        if i==len(s):
            return a 
        else:
            c=s[i]
    return a
def factor_value():
    global i
    if s[i]=='(':
        i+=1
        return expression_value()
    else:
        temp_i=i 
        while s[i].isdigit():
            i+=1
            if i==len(s):
                break
        return int(s[temp_i:i])
s='(((3+2)*2-8)-8)*3+20'
i=0
print(expression_value())

输出为2,正确

但是这种做法有个bug,就是把没必要加括号的加了括号就会只计算括号里的
比如(8/4)-6得到2,但是8/4-6得到-4,此bug留待有缘再来破。。。

第三种解法:栈

  1. 中缀表达式转后缀表达式(栈存放操作符)
  2. 计算后缀表达式的值(栈存放数)

中缀转后缀的规则:(这里借鉴自csdn的某位作者)

中缀表达式a + b*c + (d * e + f) * g,其转换成后缀表达式则为a b c * + d e * f  + g * +。

转换过程需要用到栈,具体过程如下:

1)如果遇到操作数,我们就直接将其输出。

2)如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。

3)如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。

4)如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) "的情况下我们才弹出" ( ",其他情况我们都不会弹出" ( "。

5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。

上代码,其中中缀转后缀的函数是参考这个规则写的,感觉还挺麻烦,写了快一个小时。不过最后的结果还是感到舒适。

def infix2suffix(s):
    l=[]
    out=[]
    i=0
    while i<len(s):
        if s[i].isdigit():
            temp_i=i
            while  i<len(s) and s[i].isdigit():
                i+=1
            out.append(int(s[temp_i:i]))
            i-=1
            if i==len(s)-1:
                for t in l[::-1]:
                    out.append(t)
                break
        else:
            if i==len(s)-1:
                for t in l[::-1]:
                    out.append(t)
                break
            if s[i]=='(':
                l.append('(')
            elif s[i]==')':
                while True:
                    p=l.pop()
                    if p=='(':
                        break
                    else:
                        out.append(p)
            elif s[i] in '*/':
                if len(l)==0:
                    l.append(s[i])
                else:
                    while True:
                        if len(l)==0:
                            break
                        p=l.pop()
                        if p in '(+-':
                            l.append(p)
                            break
                        else:
                            out.append(p)
                    l.append(s[i])
            elif s[i] in '+-':
                if len(l)==0:
                    l.append(s[i])
                else:
                    while True:
                        if len(l)==0:
                            break
                        p=l.pop()
                        if p=='(':
                            l.append(p)
                            break
                        else:
                            out.append(p)
                    l.append(s[i])
        i+=1
    if '(' in out:
        out.remove('(')
    return out
def suffix2result(lst):
    l=[]
    for i in lst:
        if isinstance(i,int):
            l.append(i)
        else:
            a=l.pop()
            b=l.pop()
            if i=='+':
                l.append(b+a)
            elif i=='-':
                l.append(b-a)
            elif i=='*':
                l.append(b*a)
            else:
                l.append(b//a)
    return l.pop()
l=infix2suffix('(2+3)*(5+7)+9/3-((8/4)-6)')
print(l)
print(suffix2result(l))

输出:
> [2, 3, '+', 5, 7, '+', '*', 9, 3, '/', '+', 8, 4, '/', 6, '-', '-']
> 67

这种做法就很好了,无论怎么加括号都会得到正确的逆波兰表达式和结果。

中缀转后缀用到的栈存放符号,根据一系列规则生成正确的后缀表达式。后缀表达式求值用到的栈存放数,这个过程就很简单了。

 

最后要问的是有没有人知道递归解法我的代码哪里出了bug?请在下面留言,感激不尽~

  • 11
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值