数据结构 | 二叉树的应用

目录

一、解析树

二、树的遍历


一、解析树

我们可以用解析树来表示现实世界中像句子或数学表达式这样的构造。

我们可以将((7+3)*(5-2))这样的数学表达式表示成解析树。这是完全括号表达式,乘法的优先级高于加法和减法,但因为有括号,所以在做乘法前必须先做括号内的加法和减法。树的层次性有助于理解整个表达式的计算次序。在计算顶层的乘法前,必须先计算子树中的加法和减法。

构建解析树的第一步是将表达式字符串拆分成标记列表。需要考虑4种标记:左括号、右括号、运算符和操作数。我们知道,左括号代表新表达式的起点,所以应该创建一颗对应该表达式的新树。反之,遇到右括号则意味着到达该表达式的终点。我们也知道,操作数既是叶子节点,也是其运算符的子节点。此外,每个运算符都有左右子节点。

有了上述信息,便可以定义以下4条规则:

(1)如果当前标记是(,就为当前节点添加一个左子节点,并下沉至该子节点;

(2)如果当前标记在列表['+','-','/','*']种,就将当前节点的值设为当前标记对应的运算符;为当前节点添加一个右子节点,并下沉至该子节点;

(3)如果当前标记是数字,就将当前节点的值设为这个数并返回至父节点;

(4)如果当前标记是),就跳到当前节点的父节点。

**追踪父节点的方法:**在遍历这棵树时使用栈记录父节点。每当要下沉至当前节点的子节点时,先将当前节点压到栈中。当要返回当前节点的父节点时,就将父节点从栈中弹出来。

解析树构建器代码如下:

python 复制代码
from pythonds.basic import Stack
from pythonds.trees import BinaryTree

def bulidParseTree(fpexp):
    fplist=fpexp.split()
    pStack=Stack()
    eTree=BinaryTree('')
    pStack.push(eTree)
    currentTree=eTree
    for i in fplist:
        if i=='(':
            currentTree.insertLeft('')
            pStack.push(currentTree)
            currentTree=currentTree.getLeftChild()
        elif i not in '+-*/)':
            currentTree.setRootVal(eval(i))
            parent=pStack.pop()
            currentTree=parent
        elif i in '+-*/':
            currentTree.setRootVal(i)
            currentTree.insertRight('')
            pStack.push(currentTree)
            currentTree=currentTree.getRightChild()
        elif i ==')':
            currentTree=pStack.pop()
        else:
            raise ValueError("Unknown Operator: "+i)
    return eTree

计算二叉解析树的递归函数:

python 复制代码
def evaluate(parseTree):
    opers={'+':operator.add,'-':operator.sub,'*':operator.mul,'/':operator.truediv}
    leftC=parseTree.getLeftChild()
    rightC=parseTree.getRightChild()
    if leftC and rightC:
        fn=opers[parseTree.getRootVal()]
        return fn(evaluate(leftC),evaluate(rightC))
    else:
        return parseTree.getRootVal()

二、树的遍历

我们将对所有节点的的访问称为"遍历",共有3种遍历方式,分别为前序遍历、中序遍历和后序遍历

前序遍历:

在前序遍历中,先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。

中序遍历:

在中序遍历中,先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。

后序遍历:

在后序遍历中,先递归地后序遍历左子树,然后递归地后序遍历右子树,最后访问根节点。

遍历树的代码格外简洁,这主要是因为遍历是递归的。

将前序遍历算法实现为外部函数:

python 复制代码
def preorder(tree):
    if tree:
        print(tree.getRootVal())
        preorder(tree.getLeftChild())
        preorder(tree.getRightChild())

将前序遍历算法实现为BinaryTree类的方法:

python 复制代码
def preorder(self):
    print(self.key)
    if self.leftChild:
        self.left.preorder()
    if self.rightChild:
        self.right.preorder()

后序遍历函数:

python 复制代码
def postorder(tree):
    if tree!=None:
        postorder(tree.getLeftChild())
        postorder(tree.getRightChild())
        print(tree.getRootVal())

后序求值函数:

python 复制代码
def postordereval(tree):
    opers={'+':operator.add,'-':operator.sub,'*':operator.mul,'/':operator.truediv}
    res1=None
    res2=None
    if tree:
        res1=postordereval(tree.getLeftChild())
        res2=postordereval(tree.getRightChild())
        if res1 and res2:
            return opers[tree.getRootVal()](res1,res2)
        else:
            return tree.getRootVal()

中序遍历函数:

python 复制代码
def inorder(tree):
    if tree!=None:
        inorder(tree.getLeftChild())
        print(tree.getRootVal())
        inorder(tree.getRightChild())

修改后的中序遍历函数,它能还原完全括号表达式:

python 复制代码
def printexp(tree):
    sVal=""
    if tree:
        sVal='('+printexp(tree.getLeftChild())
        sVal=sVal+str(tree.getRootVal())
        sVal=sVal+printexp(tree.getRightChild())+')'
    return sVal
相关推荐
Kalika0-037 分钟前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
_.Switch39 分钟前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
代码雕刻家1 小时前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构
一个闪现必杀技1 小时前
Python入门--函数
开发语言·python·青少年编程·pycharm
小鹿( ﹡ˆoˆ﹡ )1 小时前
探索IP协议的神秘面纱:Python中的网络通信
python·tcp/ip·php
卷心菜小温2 小时前
【BUG】P-tuningv2微调ChatGLM2-6B时所踩的坑
python·深度学习·语言模型·nlp·bug
陈苏同学2 小时前
4. 将pycharm本地项目同步到(Linux)服务器上——深度学习·科研实践·从0到1
linux·服务器·ide·人工智能·python·深度学习·pycharm
唐家小妹2 小时前
介绍一款开源的 Modern GUI PySide6 / PyQt6的使用
python·pyqt
小字节,大梦想2 小时前
【C++】二叉搜索树
数据结构·c++
羊小猪~~3 小时前
深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)
pytorch·python·rnn·深度学习·机器学习·数据分析·lstm