二叉树的简单应用 表达式树

二叉树的简单应用: 表达式树

这一应用主要利用二叉树的结构.

假如现在有一个数学公式:(2-3)*(4+5)

抛弃括号以及优先级的概念,仅仅改变符号的顺序** ∗ − 23 + 45 *-23+45 23+45**

公式中的操作符提前了,每个操作符后面跟着两个操作数,从左向右遍历就可以得到唯一的计算步骤,这就是波兰表达式

如果有以下中缀表达式:

(2-3)*(4+5)

为了快速求取先缀以及后缀表达式,我们首先把括号补全,变成下面这样:

((2-3)*(4+5))

然后把所有操作符放在它所对应的左括号的前面,就是这样:

*(-(2 3)+(4 5))

最后把括号去掉,变成这样:

*** - 2 3 + 4 5**

这就是先缀表达式,同理可以获取后缀表达式。

一. 二元表达式和二叉树

​ 数学表达式(算术表达式)具有分层次的递归结构,一个运算符作用于相应运算对象,其运算对象有可以是任意复杂的表达式.二叉树的递归结构正好用来表示这种表达式,二叉树中结点和子树的关系可用于表示运算符对运算对象的作用关系.

只包含二元运算符的表达式称为二元表达式,实际上,这里提出的计数其应用范围远不止二元表达式,完全可以用于表示一般的数学表达式,逻辑表达式等.算术表达式是二元数学表达式的特殊情况,其中的基本表达式都是数.

二元表达式可以很自然地映射到二叉树

  • 以基本运算对象(数和变量)作为叶节点中的数据
  • 以运算符作为分支结点的数据
    • 其两棵子树是它的运算对象
    • 子树可以是基本运算对象,也可以是任意复杂的二元表达式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yLELqGGt-1610447298310)(C:\Users\93623\AppData\Roaming\Typora\typora-user-images\image-20210112163727645.png)]

一个结构正确的二元表达式对应于一棵满二叉树

先根序遍历:x-ab+/cde 表达式的前缀表示(波兰表达式)

后根序遍历:ab-cd/e+x 表达式的中缀表示(逆波兰表达式)

中根序遍历:a-bxc/d+e 表达式的后缀表示

二. 构造表达式

由于建立起来的数学表达式绝不会变化,数学运算和操作都是基于已有表达式构造新表达式,因此,一种合理方式就是把它实现为一种"不变的"二叉树,下面用python的tuple作为实现基础,用三元的tuple实现二叉树结点

为使有关的表示更简洁清晰,对上面提出的二叉树的表示做一点修改,在二元表达式对应的tuple里,基本运算对象(数或变量)将直接放在空树的位置,作为基本对象.表达式 3 ∗ ( 2 + 5 ) 3*(2+5) 3(2+5) 直接映射到二叉树是

(’*’, (3,None, None),(’+’, (2, None, None), (5, None, None)))

这样表示总会出现大量没有实际意义的None,为了避免这种情况,下面将其简化为带括号的前缀表达式,括号表示运算符的作用范围

(’*’,3,(’+’,2, 5))

采用这种修改的表达方式, 表达式由两种结构组成:

  • 如果是序对(tuple),就是运算符作用于运算对象的复合表达式
  • 否则就是基本表达式,也就是数或者变量

上述两条可用于解析表达式的结构,实现对表达式的处理.

# 定义几个表达式构造函数
def make_sum(a,b):
    return ('+', a, b)

def make_pro(a,b):
    return ('*', a, b)

def make_diff(a,b):
    return ('-', a, b)

def make_div(a, b):
    return ('/', a, b)


下面语句构造出一个简单的算术表达式:

el = make_pro(3, make_sum(2,5))

显然,采用这种结构可以构造出具有复杂结构的表达式,用字符串表示变量,就能构造出各种代数表达式,例如:

make_sum(make_prod(‘a’,a), make_prod(‘b’, 7))

在定义表达式处理函数时,经常需要区分基本表达式(直接处理)和复合表达式(递归处理), 为分辨这两种情况,定义一个判别是否为基本表达式的函数:

def is_basic_exp(a):
	return not isinstance(a, tuple)

def is_number(x):
    return (isinstance(x, int) or isinstance(x, float) isinstance(x,complex))

三.表达式求值

前面定义的二元表达式是简单数学表达式的一种python实现,在这些定义的基础上,可以根据需要实现各种表达式操作.例如可以定义python函数求两个二元表达式的和,乘积等,这里考虑一个求表达式值得函数.

表达式求值的规则:

  • 对表达式里的数和变量,其值就是它们自身
  • 其他表达式根据运算符的情况处理,可以定义专门处理函数
  • 如一个运算符的两个运算对象都是数,就可以求出一个数值

还有些情况,如加数是0,乘数是0或1,可对有关的部分表达式做求值和化简.总之,所谓求值,就是对表达式做一些简单化简,把能计算的都算出来

下面是求值函数的基本部分:

def eval_exp(e):
    if is_basic_exp(e):
        return e
    op, a, b = e[0], eval_exp(e[1]), eval_exp(e[2]) # **
    if op == '+':
        return eval_sum(a,b)
    elif op == '-':
        return eval_diff(a,b)
    elif op == '*':
        return eval_prod(a, b)
    elif op == '/':
        return eval_div(a,b)
    else:
        raise ValueError("Unknown operator:", op)
        
def eval_sum(a, b):
    if is_number(a) and is_number(b):
        return a+b
    if is_number(a) and a == 0:
        return b
    if is_number(b) and b == 0:
        return a
    return make_sum(a,b)

def eval_div(a, b):
    if is_number(a) and is_number(b):
        return a / b
    if is_number(a) and a == 0:
        return 0
    if is_number(b) and b == 1:
        return a
    if is_number(b) and b == 0:
        raise ZeroDivisonError
    return make_div(a, b)

这里的基本方法就是基于实际运算符,把不同计算分发给具体函数处理,其中特别值得注意的是标有"**" 的语句,其中通过对本函数的两个递归调用处理子表达式, 完成可能的计算,后面可以看到,对二叉树的所有递归处理都将采用这种模式

在最后一行引发异常时,给异常ValueError提供了两个实参,实际上,在引发异常时可以送给它任意多个实参,如果没有其他处理,在python解释器显示异常信息时,将输出这些实际参数的值,例如,对上面情况,它输出字符串Unknown operator和op的实际值,如果希望捕捉并处理这种异常,可以通过python提供的特殊机制取得这些实参的值,

异常时,给异常ValueError提供了两个实参,实际上,在引发异常时可以送给它任意多个实参,如果没有其他处理,在python解释器显示异常信息时,将输出这些实际参数的值,例如,对上面情况,它输出字符串Unknown operator和op的实际值,如果希望捕捉并处理这种异常,可以通过python提供的特殊机制取得这些实参的值,

猜你喜欢

转载自blog.csdn.net/weixin_46129834/article/details/112543628