树的遍历
三种遍历方式
- 前序遍历
首先访问根节点,然后递归地做左侧子树的前序遍历,随后是右侧子树的递归前序遍历。 - 中序遍历
递归地对左子树进行一次遍历,访问根节点,最后递归遍历右子树。 - 前序遍历
递归地对左子树和右子树进行后序遍历,然后访问根节点。
前序遍历
树的遍历代码十分简洁,主要是因为调用递归。
外部函数
将二叉树作为参数。外部函数特别优雅,因为我们的基本情况只是检查树是否存在。如果树参数为 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