数据结构与算法Python版 树的遍历与应用


一、树的遍历

树的遍历Tree Traversals

  • 对一个数据集中的所有数据项进行访问的操作称为遍历Traversal
  • 按照对节点访问次序的不同来区分3种遍历
    • 前序遍历(preorder):先访问根节点,再递归地前序访问左子树、最后前序访问右子树;
    • 中序遍历(inorder):先递归地中序访问左子树,再访问根节点,最后中序访问右子树;
    • 后序遍历(postorder):先递归地后序访问左子树,再后序访问右子树,最后访问根节点。

树的遍历:递归算法代码

def preorder(tree: BinaryTree):
    """前序遍历"""
    if tree:
        print(tree.get_root_val(), end=" ")
        preorder(tree.get_left_child())
        preorder(tree.get_right_child())


def inorder(tree: BinaryTree):
    """中序遍历"""
    if tree:
        inorder(tree.get_left_child())
        print(tree.get_root_val(), end=" ")
        inorder(tree.get_right_child())


def postorder(tree: BinaryTree):
    """后序遍历"""
    if tree:
        postorder(tree.get_left_child())
        postorder(tree.get_right_child())
        print(tree.get_root_val(), end=" ")

测试:对下图分别进行前中后序遍历
在这里插入图片描述

my_tree = BinaryTree("a")
my_tree.insert_left("b")
my_tree.insert_right("c")
my_tree.get_right_child().set_root_val("hello")
my_tree.get_left_child().insert_left("d")
print(my_tree)

print("\n前序遍历:")
preorder(my_tree)
print("\n中序遍历:")
inorder(my_tree)
print("\n后序遍历:")
postorder(my_tree)


### 输出结果
[a, [b, [d, None, None], None], [hello, None, None]]

前序遍历:
a b d hello
中序遍历:
d b a hello
后序遍历:
d b hello a

二、树的应用-表达式解析树

表达式解析树

  • 将表达式表示为树结构:叶节点保存操作数内部节点保存操作符
  • 表达式层次决定计算的优先级:越底层的表达式,优先级越高
  • 树中每个子树都表示一个子表达式。将子树替换为子表达式值的节点,即可实现求值

在这里插入图片描述

建立表达式解析树-算法

  • 从左到右扫描全括号表达式的每个单词,依据规则建立解析树
    • 如果当前单词是"(":为当前节点添加一个新节点作为其左子节点,当前节点下降为这个新节点
    • 如果当前单词是操作符"+,-,/,*":将当前节点的值设为此符号,为当前节点添加一个新节点作为其右子节点,当前节点下降为这个新节点
    • 如果当前单词是操作数:将当前节点的值设为此,当前节点上升到父节点
    • 如果当前单词是")":则当前节点上升到父节点

示例:全括号表达式(3+(4*5))解析树的建立过程

在这里插入图片描述

建立表达式解析树-实现

  • 创建左右子树可调用insertLeft/Right;当前节点设置值,可以调用setRootVal;下降到左右子树可调用getLeft/RightChild
  • 上升到父节点功能:用一个栈来记录跟踪父节点,当前节点下降时,将下降前的节点push入栈;当前节点需要上升到父节点时,pop出栈的节点
def build_parse_tree(expression):
    """
    BinaryTree类:使用链表法实现
    Stack类:参考《线性结构与栈》中的定义
    """
    root = BinaryTree("")
    s = Stack()
    # 将全括号表达式分割成列表。约定:操作数是一位数
    expr_list = list(expression)
    s.push(root)
    cur_node = root
    for i in expr_list:
        if i == "(":
            cur_node.insert_left("")
            s.push(cur_node)
            cur_node = cur_node.get_left_child()
        elif i in ["+", "-", "*", "/"]:
            cur_node.set_root_val(i)
            cur_node.insert_right("")
            s.push(cur_node)
            cur_node = cur_node.get_right_child()
        elif i == ")":
            cur_node = s.pop()
        else:
            # 操作数
            cur_node.set_root_val(i)
            cur_node = s.pop()

    return root


expression = "(3+(4*5))"
parse_tree = build_parse_tree(expression)
print(parse_tree)


### 输出结果
[+, [3, None, None], [*, [4, None, None], [5, None, None]]]

从表达式解析树生成全括号中缀表达式

  • 采用中序遍历递归算法,来生成全括号中缀表达式
def print_expr(parse_tree: BinaryTree):
    """中序遍历-生成全括号中缀表达式"""
    res = ""
    if parse_tree:
        if parse_tree.get_left_child() and parse_tree.get_right_child():
            # 左子树
            res = "(" + print_expr(parse_tree.get_left_child())
            # 根节点
            res = res + str(parse_tree.get_root_val())
            # 右子树
            res = res + print_expr(parse_tree.get_right_child()) + ")"
        else:
            res = res + str(parse_tree.get_root_val())

    return res


expression = "(3+(4*5))"
parse_tree = build_parse_tree(expression)
print(parse_tree)
print(print_expr(parse_tree))


### 输出结果
[+, [3, None, None], [*, [4, None, None], [5, None, None]]]
(3+(4*5))

三、树的应用-表达式解析树求值

表达式解析树求值-递归算法一

  • 二叉树是一个递归数据结构,用递归算法对表达式解析树求值
    • 基本结束条件:叶节点是最简单的子树,没有左右子节点,其根节点的数据项即为子表达式树的值
    • 缩小规模:将表达式树分为左子树、右子树,即为缩小规模
    • 分别计算左子树和右子树的值,然后将左右子树的值依根节点的操作符进行计算,从而得到表达式的值
def evaluate(parse_tree: BinaryTree):
    left_child = parse_tree.get_left_child()
    right_child = parse_tree.get_right_child()
    if left_child and right_child:
        return str(
            # eval方法用来计算字符串表达式的值
            eval(
                evaluate(left_child) + parse_tree.get_root_val() + evaluate(right_child)
            )
        )
    else:
        return parse_tree.get_root_val()


expression = "(3+(4*5))"
parse_tree = build_parse_tree(expression)
print(parse_tree)
res = evaluate(parse_tree)
print(res)


### 输出结果
[+, [3, None, None], [*, [4, None, None], [5, None, None]]]
23

表达式解析树求值-递归算法二

  • 采用与后序遍历相似的方式
def postorder_eval(parse_tree: BinaryTree):
    if parse_tree:
        res1 = postorder_eval(parse_tree.get_left_child())
        res2 = postorder_eval(parse_tree.get_right_child())
        if res1 and res2:
            return str(eval(res1 + parse_tree.get_root_val() + res2))
        else:
            return parse_tree.get_root_val()
        
        
expression = "(3+(4*5))"
parse_tree = build_parse_tree(expression)
print(parse_tree)
print(postorder_eval(parse_tree))


### 输出结果
[+, [3, None, None], [*, [4, None, None], [5, None, None]]]
23

您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值