数据结构与算法笔记(10) - 表达式

本文探讨了数学表达式的不同表示方式,包括中缀、前缀和后缀表达式。中缀表达式常见但难以表示所有运算顺序,前缀和后缀表达式则能自然描述计算顺序。后缀表达式尤其适合计算机处理,其计算过程简单,主要使用栈来实现。同时,文章介绍了如何将中缀表达式转换为后缀表达式,以便进行计算。
概述

本文所讨论的表达式指的是数学表达式。在数学表达式中,最常见的结构是由一组二元运算符和运算对象构成,通常,二元运算符写在运算对象中间,这样的形式成为中缀表达式。将运算符写在运算对象前面称为前缀表达式,将运算符写在运算对象的后面称为后缀表达式。
本文对表达式的讨论是假设运算符为二元运算符。

中缀表达式

中缀表达式是日常最习惯的一种表达式,但是作为表达式的表示方式,它很难统一的贯彻,对于一元和多元运算符难以用中缀表达式表示。
在运算符的元数唯一且确定的前提下,描述表达式的最重要的问题是准确描述表达式的顺序,而中缀表达式不足以表示所有的可能的运算顺序,它需要借助其他的辅助措施,比如辅助符号、约定的优先级等。

前缀表达式

前缀表达式,也成为波兰表达式,是由波兰数学家 J. Lukasiewicz 于1929年提出的,它将所有的运算符都写在他们的运算对象前面。每个运算符的运算对象,就是它后面出现的几个完整表达式,表达式个数由运算符的元数确定。这样的规定完全不需要引入括号,它已自然地描述了计算顺序。

后缀表达式

后缀表达式,也被称为逆波兰表达式,它与前缀表达式类似,只不过运算符和运算对象的位置相反。其中所有运算符都写在它们的运算对象之后。每个运算符的运算对象,就是它前面出现的几个完整表达式,个数也是由元素安抚的元数确定的,同样它也自然的描述了复杂表达式的计算顺序而不需要引入其他辅助机制。
后缀表达式特别适合计算机处理。

举个栗子

一下三种表达形式描述的是同一个计算过程

中缀形式: (3 - 5) * (1 + 2 * 4) / 6
前缀形式: / * - 3 5 + * 2 4 1 6
后缀形式: 3 5 - 1 2 4 * + * 6 /
表达式的计算

假定我们要处理的是简单的算术表达式,运算对象都是浮点数表示的数,只有四个运算符 ‘+’,’-’,’*’,’/’

后缀表达式的计算

后缀表达式的计算过程比较简单,步骤如下:

  • 遇到运算对象,记录以备后面使用
  • 遇到运算符,根据元数取出响应的几个运算对象,计算结果并保存

算法的基本结构就是上面这样,我们使用栈保存运算对象,使用python语言描述为:

# 定义一个栈,用来保存运算对象
class Stack(object):
    def __init__(self):
        self._container = []

    def push(self, value):
        self._container.append(value)

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

    def top(self):
        return self._container[-1]

    def is_empty(self):
        return not self._container

    def depth(self):
        return len(self._container)


# 需要额外规定用户的输入用空格来区分运算符和运算对象
# 否则,需要额外处理
def suffix_exp_evaluator(line):
    operators = '+-*/'
    st = Stack()

    for x in exp:
        if x not in operators:
            st.push(float(x))
            continue
        if st.depth() < 2:
            raise SyntaxError
        a = st.pop()
        b = st.pop()
        if x == '+':
            res = a + b
        elif x == '-':
            res = a - b
        elif x == '*':
            res = a * b
        elif x == '/':
            res = a / b
        st.push(res)
        
    if st.depth() == 1:
        return st.pop()
    raise SyntaxError


# 程序入口
def main():
    while True:
        try:
            line = input('Suffix Expression: ')
            if line == 'end': return
            res = suffix_exp_evaluator(line.split(''))
            print(res)
        except Exception as e:
            print('Error: ', type(e), e.args)
中缀表达式转换

中缀表达式是一种处理起来比较复杂的表达式,因为上面已经定义了后缀表达式,现在将中缀表达式转换成后缀表达式后就可以利用后缀表达式求值了。

转换步骤:

  • 扫描中缀表达式,如果遇到运算对象,直接送出;
  • 如果是运算符,需要和上一个尚未送出的运算符比较,优先级高的送出;
  • 如果是左括号,需要记录,确保一个优先计算的子表达式的七点
  • 如果是右括号,需要把记录的运算符逐个弹出,直到遇到左括号也弹出;
  • 扫描结束后,记录的运算符需要全部送出到后缀表达式。

转换代码实现:

# 定义运算符优先级
priority = {'(': 1, '+': 3, '-': 3, '*': 5, '/': 5}
infix_operators = '+-*/()'


# 中缀表达式转换成后缀表达式
def trans_infix_suffix(line):
    st = Stack()
    exp = []

    for x in line:
        if x not in infix_operators:  # 运算对象直接送出
            exp.append(x)
        if st.is_empty() and x == '(':  # 左括号进栈
            st.push(x)
        elif x == ')':
            while not st.is_empty() and st.top != '(':
                exp.append(st.pop())
            if st.is_empty():  # 未找到左括号,表达式有问题
                raise SyntaxError
            st.pop()  # 弹出左括号,不进栈
        else:
            # 比当前运算符优先级大的运算符送出
            while not st.is_empty() and priority[st.top()] >= priority[x]:
                exp.append(st.pop())
            st.push(x)

    while not st.is_empty():
        if st.top() == '(':
            raise SyntaxError
        exp.append(st.pop())

    return exp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值