树的遍历

树的遍历

三种遍历方式

  1. 前序遍历
    首先访问根节点,然后递归地做左侧子树的前序遍历,随后是右侧子树的递归前序遍历。
  2. 中序遍历
    递归地对左子树进行一次遍历,访问根节点,最后递归遍历右子树。
  3. 前序遍历
    递归地对左子树和右子树进行后序遍历,然后访问根节点。

前序遍历

树的遍历代码十分简洁,主要是因为调用递归。

外部函数

将二叉树作为参数。外部函数特别优雅,因为我们的基本情况只是检查树是否存在。如果树参数为 None,那么函数返回而不采取任何操作。

def preorder(tree):
	if tree:
		print(tree.getRootVal())
		preorder(tree.getLeftChild())
		preorder(tree.getRightChild())

内部函数

将代码从内部移动到外部时会发生什么?

  • 用 self 替换 tree 。
  • 内部方法必须在进行前序的递归调用之前检查左和右孩子的存在。
def in_preorder(self):
    print(self.key)
    if self.leftChild:
        self.leftChild.preorder()
    if self.rightChild:
        self.rightChild.preorder()

哪种方式实现前序最好?

实现前序作为外部函数可能更好
很少只是想遍历树
使用其中一个基本的遍历模式来完成其他任务

后序遍历

def postorder(tree):
    if tree != None:
        postorder(tree.getLeftChild())
        postorder(tree.getRightChild())
        print(tree.getRootVal())

与前序遍历几乎相同,只是更改了 print 的位置。

后序遍历的常见用法,即计算分析树。

假设我们的二叉树只存储表达式树的数据,让我们重写计算函数。

def postordereval(tree):
    opers = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truedi
             v}
    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()

可以看出,两端代码形式相同,只是不在函数的末尾打印值,而是返回它。

中序遍历

在所有三个遍历函数中,我们只是改变 print 语句相对于两个递归函数调用的位置。

def inorder(tree):
    if tree != None:
        inorder(tree.getLeftChild())
        print(tree.getRootVal())
        inorder(tree.getRightChild())

如果我们执行一个简单的中序遍历分析树,我们得到没有任何括号的原始表达式。 让我们修改基本的 inorder 算法,以允许我们恢复表达式的完全括号版本。 我们将对基本模板进行的唯一修改如下:在递归调用左子树之前打印左括号,并在递归调用右子树后打印右括号。

def printexp(tree):
    sVal = ""
    if tree:
        sVal = '(' + printexp(tree.getLeftChild())
        sVal = sVal + str(tree.getRootVal())
        sVal = sVal + printexp(tree.getRightChild())+')'
    return sVal
posted @ 2019-05-27 12:18  banshaohuan  阅读(271)  评论(0编辑  收藏  举报