二叉树的遍历

所谓二叉树的遍历,就是把二叉树的所有树节点依次输出,每个节点只输出一次,且所有节点都被输出。我们常见的遍历方式一共是四种:前序遍历,中序遍历,后序遍历,以及层次遍历。概念上,前三种一般被称为深度优先遍历(与深度优先搜索的逻辑是一致的,关于深度优先搜索详见:),而层次遍历一般被称为广度优先遍历。在lintcode上,就这四种方法,就分别有一道题目与之对应。当然,题目永远是次要,通过题目理解二叉树的构造,学习深搜程序的基本写作流程,才是我们应该学习的重点。


先说深度优先遍历。前序遍历,中序遍历,后序遍历,不同之处在于输出节点的顺序,具体的:
1. 前序(pre-order):先根后左再右
2. 中序(in-order):先左后根再右
3. 后序(post-order):先左后右再根


下面这幅图就特别形象了:

二叉树的遍历_第1张图片

好吧,我承认我是截图的,别人给的很全,最后也给出了层次遍历的顺序(这个先不说)。举例前序遍历,也就是说对扫描到的每一个节点都采取先输出他自己,再扫描左孩子(对左孩子一样使用前序遍历),再扫描右孩子(同理,对右孩子前序遍历)。这就不用说是什么逻辑了吧——当然是我们说过无数遍的递归。中序、后序也是同理。


因此,用递归能非常简单的写出三种遍历方式的代码:

# preorder
def preorderTraversal(root):
    if root is None:
        return []
    return [root.val] + preorderTraversal(root.left) + preorderTraversal(root.right)


# inorder
def inorderTraversal(root):
    if root is None:
        return []
    return inorderTraversal(root.left) + [root.val] + inorderTraversal(root.right)


# postorder
def postorderTraversal(root):
    if root is None:
        return []
    return postorderTraversal(root.left) + postorderTraversal(root.right) + [root.val]


递归的思想不多做解释了,这个逻辑就跟自然语言的描述是一样的(Python更接近这种自然描述)。需要注意的一点是“触底”后程序的反应,因为这里没有设置全局变量,所以返回的是一个空列表。这个空列表是要参与到之后“回溯”的运算中的。但是,如果设置了一个全局变量存储输出的节点值,“触底”的做法就不一样了。类似于我们在“二叉树的所有路径中”使用的递归算法,要求“触底”时直接return(也就是什么都不做)。


所以,程序也可以写成这种形式:


# result = []
# postorderTraversal(root, result)
# print(result)


def postorderTraversal(root, result):
    if root is None:
        return
    postorderTraversal(root.left, result)
    postorderTraversal(root.right, result)
    result.append(root.val)


这里定义的后序遍历函数其实是改变了列表result,所以什么都不返回(相当于C++中的void函数)。“触底”的条件依然是节点为空,此时,“触底”后,什么都不做。运行程序时可执行最上边的注释给出的三行代码,即可看到输出结果。

然而,“递归虽易,面试却难”。递归算法由于其效率方面的天然缺点,并不受面试官欢迎。我们一般使用迭代来实现遍历。这个以后会讲解。




你可能感兴趣的:(二叉树,遍历)