中缀表达式To前缀表达式 (python实现)

1.名词解释

        1.1 中缀表达式

                        普通表达式,即操作符位于操作数的中间。如''2+3*5'',''(2+3)*5''。这种表达式的特点是根据运算符的优先级不同,计算顺序不同。可以通过添加括号来改变计算的顺序,这种表达式人类理解起来没什么问题,但计算机识别起来就有点困难。

        1.2 全括号表达式

                        为了方便计算机识别表达式,可以将中缀通过添加括号的方法转化为全括号表达式,即每一次运算都添加一对括号来保证计算的优先级。如"(2+(3*5))","((2+3)*5)",全括号表达式解决了计算机识别的问题。但是又出现了新的问题,括号太多,增加了处理负担。显得不够优雅,发现了问题,自然就会诞生解决问题的牛人,1929年波兰的逻辑学家卢卡西维兹(Jan Lucasiewicz)提出了将运算符放在运算项前面的逻辑表达式,又称“波兰表达式”。优雅地地解决了这个问题

        1.3 前缀表达式(波兰式)

                        波兰式表达式,就是将运算符放在两个运算数的前面的表达式,如"+ 2 * 3 5","*+ 2 3 5"。可以发现波兰式完全消灭了括号,而且还能保证运算顺序和对应的中缀表达式相同。这里也解释一下波兰式的运算规则,(后续将利用这个规则进行波兰式的求值。)如果一个操作符的后面是两个操作数,则直接运算,如果是一个操作数+一个操作符,说明要先运算第二个操作符。再将结果与第一个数进行运算得到结果 。

eg:"+ 2 * 3 5"

第一个操作符 “+” ,接下来是操作数 2,

        接下来又是操作符’* ,说明要先运算”*“

                接下来是两个操作数 3 和 5 ,作乘法的结果是15

        将结果15作为’+“的第二个操作数
得到2+15 =17

        1.4 后缀表达式(逆波兰式)

                        所谓逆波兰式,就是将运算符放在两个运算数的后面的表达式,如"2 3 5 * +","2 3 +5*"。

2.如何把中缀表达式转化为波兰式

        2.1 先转化为全括号表达式

               如:"2+3*5''

                得到全括号表达式:"(2+(3*5))''

                观察全括号表达式,得到转化的方法:将运算符替换掉对应的左括号,同时删除右括号

         2.2 利用栈的后进先出的特性来转化

2.2.1 先用python来定义一个栈的抽象数据类型

代码如下:

class Stack:
    def __init__(self):
        self.items = []

    def push(self,item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def isEmpty(self):
        return self.items == []

    def size(self):
        return len(self.items)

    def peek(self):
        return self.items[-1]

    def __str__(self):
        return '{}←'.format(self.items)

     2.2.2接下来定义一个函数来处理输入数据。将中缀表达式的字符串转成一个列表,方便后续处理

def inputProcess(inputString):
    '''将输入的字符串转化为包含操作数和操作符的列表'''
    resList = []
    tmp = ''  # 用于保存多位的数字时的中间结果
    for ele in inputString:
        if  ele.isdigit() or ele == '.': # 能处理小数点的情况
            tmp += str(ele)
        else:
            if tmp != '':
                resList.append(tmp)
                tmp = ''
            resList.append(ele)
    if tmp != '':
        resList.append(tmp)
    return resList

    这里有一个问题,将一个字符串变为列表,可能有更加简单的方法,如             

# 字符串转列表
lis = list('(1+2+3)*9')
print(lis)
>>>['(', '1', '+', '2', '+', '3', ')', '*', '9']   # 结果正确
# 但是不能处理如下情况
lis1 = list('(10+28+3)*9')
print(lis1)
>>>['(', '1', '0', '+', '2', '8', '+', '3', ')', '*', '9'] # 10,28 被分割成了两个元素

     2.2.3    编写转化的主函数

def inToPreExp(epxString):
    expList = inputProcess(epxString)
    s = Stack()
    optPriorityDict = {')':0,'+':1,'-':1,'*':2,'/':2}  # 操作符优先级字典
    res = [] # 保存结果
    for ele in expList[::-1]:  #  逆序扫描列表
        if ele.isdigit():  # 1数字直接添加进结果字符串
            res.insert(0,ele)   # 为保证数字顺序,所有从左边插入进结果列表
        elif ele == ')':  # 2.右括号压栈
            s.push(ele)
        elif ele =='(':  #  3.左括号,连续出栈直到右括号,并追加到结果字符串
            top = s.pop()
            while top != ')':
                res.insert(0,top)
                top = s.pop()
        elif ele in '+-*/':  # 4 操作符和栈顶元素比较优先级
            while (not s.isEmpty() and \
                    optPriorityDict[s.peek()] >= optPriorityDict[ele]): # 当前栈顶元素的优先级大于等于扫描到的运算符
                res.insert(0, s.pop())  # 出栈并插入结果列表
            s.push(ele) # 入栈

    while not s.isEmpty():
        res.insert(0,s.pop())

    return res

算法:

1.创建优先级字典,初始化一个空栈,用于保存操作符,一个空列表用于保存结果,

逆序扫描列表

2.如果遇到数字,直接插入进结果列表的最左边

3.如果是“)”:

        入栈

4.如果遇到"(":

        持续出栈,并插入到结果列表,直到遇到")"

5.如果是"+-*/":

        如果栈顶元素的优先级比当前操作符大,则连续出栈

        当前操作符入栈

6.扫描完成以后,如果栈不为空,则一次出栈.插入到结果列表

以下是一些测试

print(inToPreExp('(2+3)*5'))
print(inToPreExp('2+3*5'))
print(inToPreExp('(12-5)*(23+8)'))
print(inToPreExp('2+3*7/20-(2+3)'))


结果:
['*', '+', '2', '3', '5']
['+', '2', '*', '3', '5']
['*', '-', '12', '5', '+', '23', '8']
['+', '2', '-', '*', '3', '/', '7', '20', '+', '2', '3']

3.波兰式求值

直接上代码:

def evalPreExp(postExpList):
    # 前缀表达式求值
    s = Stack()
    for ele in postExpList[::-1]:
        if ele.isdigit():
            s.push(ele)
        else:
            op1 = s.pop()
            op2 = s.pop()
            res = toMath(ele,int(op1),int(op2))
            s.push(res)
    return s.pop()

def toMath(op,op1,op2):
    # 根据不同的运算符做四则运算
    if op == '+':
        return op1 + op2
    elif op == '-':
        return op1 - op2
    elif op == '*':
        return op1 * op2
    elif op == '/':
        return op1 / op2

print(evalPreExp(['*', '-', '12', '5', '+', '23', '8'])) # >>> 结果:217

总结:

波兰式和逆波兰式因其优雅简洁而闻名

大部分教程都是中缀到后缀的转化

尝试了一下中缀到前缀的转化

需要注意的几个重点:

1.优先级字典中设置右括号为最低优先级,   中缀转后缀时,左括号的优先级最低

2.扫描列表的时候 逆序

3.因为时逆序扫描,为保证操作数的位置不变,所以插入到结果列表的时候使用insert(0,ele)的方法从左边插入

参考书籍:

Bradley N. Miller, David L. Ranum
<<Introduction to Data Structures and Algorithms in Python>>

35岁开始学Python , 也不知道为了啥 ?

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值