IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 中缀表达式To前缀表达式 (python实现) -> 正文阅读

[Python知识库]中缀表达式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 ,?也不知道为了啥 ?

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 14:07:30  更:2021-07-22 14:10:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/6 19:32:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码