《算法导论》二叉树遍历的递归&非递归实现

二叉树常见的遍历方法有:

  • 前序遍历(preorder):“根左右”
  • 中序遍历(inorder):“左根右”
  • 后序遍历(postorder):“左右根”
  • 层次遍历(level):逐层从左到右访问节点

前三种遍历方法,属于树的深度优先搜索DFS。层次遍历方法则属于广度优先搜索BFS。

前三种对应于数学上的前缀、中缀、后缀表达式(波兰/逆波兰表示),方便计算机来计算带括号区分运算优先级别的运算。

借鉴leetcode中对于二叉树的定义,后面给出几种遍历方法的实现。完整的实现及测试代码见Github/Algorithm/Binary Tree。

# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

前序遍历

递归实现,直接按照preorder的定义即可。

def preorder_1(root):
    if root != None:
        print root.val
        preorder_1(root.left)
        preorder_1(root.right)    

preorder的非递归实现,借助栈的数据结构实现,有两种实现方法:

def preorder_2(root):
    stack = []
    stack.append(root)
    while stack:
        node = stack.pop()
        print node.val
        if node.right:
            stack.append(node.right)
        if node.left:
            stack.append(node.left)    

def preorder_3(root):
    stack = []
    while root or stack:
        if root:
            print root.val
            stack.append(root) # push root
            root = root.left # visit left
        else:
            root = stack.pop() # backtrack parent node
            root = root.right # visit right
  • 第一种方法主要是根节点出栈,访问根节点,然后压入右子节点左子节点(出栈顺序相反,才能实现访问先左后右),直到栈空。
  • 第二种方法也是基于栈,但用回溯策略,每次左子树遍历完之后才回溯,然后遍历右子树。

中序遍历

中序遍历的递归实现也是按定义直接来。非递归实现也是借助栈,采用和preorder第二种回溯相似的方法。对于BST,用中序遍历可以得到有序数列。

def inorder_1(root):
    if root != None:
        inorder_1(root.left)
        print root.val
        inorder_1(root.right)

def inorder_2(root):
    stack = []
    while root or stack:
        if root:
            stack.append(root)
            root = root.left
        else:
            root = stack.pop()
            print root.val
            root = root.right

后序遍历

后序遍历的非递归实现比较复杂,但可以借助两个栈实现。后序遍历的顺序是“左-右-根”,可以看作伪先序遍历“根-右-左”的逆过程,因此仿preorder的非递归实现,在出栈的同时压入另一个栈,可以实现反向输出“根-右-左”的顺序,即后序遍历。

def postorder_1(root):
    if root != None:
        postorder_1(root.left)    
        postorder_1(root.right)
        print root.val

def postorder_2(root):
    if root == None:
        return 
    stack = []
    output = []
    stack.append(root)
    while stack:
        node = stack.pop()
        output.append(node)
        if node.left:
            stack.append(node.left)
        if node.right:
            stack.append(node.right)
    while output:
        node = output.pop()
        print node.val

借助辅助栈带来O(logn)的空间复杂度,能不能在O(1)的空间复杂度实现?

Morris Traversal方法: Morris遍历方法用了线索二叉树,这个方法不需要为每个节点额外分配指针指向其前驱和后继结点,而是利用叶子节点中的右空指针指向中序遍历下的后继节点就可以了。这样就节省了需要用栈来记录前驱或者后继结点的额外空间, 所以可以达到O(1)的空间复杂度,但会暂时性修改树的结构。


层次遍历

层次遍历是广度优先的一种情形。BFS常常借助队列的数据结构来实现,因此层次遍历可以维护一个队列来实现。在访问当前节点时,也把它的左右子节点压入队列尾,访问完队列头(同一level)的节点后,再出队访问之前入队的下一level的一系列节点。

def level_traversal(root):
    queue = []
    if root == None:
        return
    queue.append(root)
    while queue:
        node = queue.pop(0)
        print node.val
        if node.left:
            queue.append(node.left)
        if node.right:
            queue.append(node.right)

相关题目:Binary Tree Level Order Traversal(I,II)和Binary Tree Zigzag Level Order Traversal。


求二叉树深度(递归&非递归实现)

Leetcode中Maximum Depth of Binary Tree定义二叉树的最大深度,是沿着从root到最远leaf的最长路径的节点数,大概就是层次遍历的最大层数吧?例如,只有一个节点,其深度为1;一个三层的二叉树深度为3。

求二叉树的深度,是遍历二叉树的一个拓展应用。

递归实现:很简单,分别求左右子树的深度,然后返回两者深度的最大值+1,即当前根子树的深度。时间复杂度O(n),空间复杂度取决于树的深度可能范围,介于O(logn)和O(n)之间。

def maxDepth(self, root):
    if not root:
        return 0
    return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))

非递归实现:递归实现中先处理左子树、后处理右子树、再返回最大值,这一过程和后序遍历相同。因此可以参考后序遍历的借助栈的迭代实现。

def maxDepth(self, root):
    if not root:
        return 0
    stack = []
    max_depth = 0
    stack.append(root)
    prev, cur = None, None
    while stack:
        cur = stack[-1]
        if not prev or (prev.left == cur) or (prev.right == cur):
            if cur.left:
                stack.append(cur.left)
            elif cur.right:
                stack.append(cur.right)
        elif cur.left == prev:
            if cur.right:
                stack.append(cur.right)
        else:
            stack.pop()

        prev = cur
        if len(stack) > max_depth:
            max_depth = len(stack)

    return max_depth

参考层次遍历:树的最大深度即层次遍历的层数,修改基于队列的层次遍历函数,同时记录层数,返回层数即可,效率更高。

def maxDepth(root):
    if not root:
        return 0
    queue = []
    queue.append(root)
    max_depth = 0 # level = 0
    while queue:
        size = len(queue)
        for i in xrange(size):
            node = queue.pop(0)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        max_depth += 1 # level += 1
    return max_depth

相关题目

  • Leetcode 144,Binary Tree Preorder Traversal,https://leetcode.com/problems/binary-tree-preorder-traversal/
  • Leetcode 94,Binary Tree Inorder Traversal,https://leetcode.com/problems/binary-tree-inorder-traversal/
  • Leetcode 145,Binary Tree Postorder Traversal,https://leetcode.com/problems/binary-tree-postorder-traversal/

  • Leetcode 105,Construct Binary Tree from Preorder and Inorder Traversal,https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/

  • Leetcode 106,Construct Binary Tree from Inorder and Postorder Traversal,https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/

扩展阅读

  • http://blog.csdn.net/kofsky/article/details/2886453

你可能感兴趣的:(算法笔记)