二叉树遍历方法总结

二叉树基本概念

面试的时候提到的树, 大部分都是二叉树. 所谓二叉树是树的一种特殊结构, 在二叉树中每个节点最多只能有两个子节点, 在二叉树中最重要的操作莫过于遍历, 即按照某一顺序访问树中的所有节点.

树的遍历方式

通常树有如下几种遍历方式:

  1. 前序遍历
  2. 中序遍历
  3. 后续遍历

每种遍历都对应三种实现方式, 基于递归和迭代(循环)实现的时间复杂度为\(O(n)\), 空间复杂度为\(O(n)\) (递归和迭代的空间复杂度是由栈的深度决定的, 通常情况下为\(O(logn)\), 最坏的情况为单链\(O(n)\)), 基于Morris算法的时间复杂度为\(O(n)\), 空间复杂度为\(O(1)\). (应聘者应该对这三种遍历的9种实现方式都了如指掌)

Morris通用解法

Morris遍历使用二叉树节点中大量指向null的指针, 将空间复杂度从\(O(n)\)优化到\(O(1)\), , 由Joseph Morris 于1979年发明, Morris的通用解法过程如下:

Morris的整体思路为: 从某个跟节点出发, 找到它的左子树的最右侧节点之后与这个跟节点进行连接. 由右图, 建立连接之后, cur这个指针可以完整的从一个节点顺着下一个节点遍历, 将整棵树遍历完毕, 直到7这个节点右侧没有指向.

基于递归的实现

前序遍历

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        return self.traversal(root, [])

    def traversal(self, root, res):
        if not root:
            return []
        res.append(root.val)
        self.traversal(root.left, res)
        self.traversal(root.right, res)
        return res

中序遍历

class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        def traversal(root):
            if not root:
                return []
            traversal(root.left)
            res.append(root.val)
            traversal(root.right)
            return res
        res = []
        return traversal(root)

后续遍历

class Solution(object):
    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        def traversal(root, res):
            if not root:
                return []
            traversal(root.left, res)
            traversal(root.right, res)
            res.append(root.val)
            return res
        return traversal(root, [])

递归函数传参python
python传参机制为: python不允许用户在函数的参数传递中选择是值传递还是引用传递, 而是采用的对象引用传递, 如果函数收到的是一个可变对象(如列表, 字典)的引用, 就采用引用传递, 而如果收到的是一个不可变的对象(如数值, 字符串或元组) 的引用, 就采用值传递, 在函数中将无法改变参数的原始值.

问题: 如果是值传递, 怎么保证递归可以返回该值呢, 如count=0需要在递归过程中过自加一:

  1. 使用数组初始化并作为参数传递
  2. 将count作为递归函数的参数, 并且每次都要定义res=traversal(~, count)接收每一次改变.

基于迭代的实现

前序遍历

# 自己的实现, 多叉树暂时未验证
class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        stack, res = [root], [] 
        while stack:
            node = stack.pop()
            res.append(node.val)
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        return res   
#  官方实现: (深入浅出) 先深入到最左侧支左子树终点, 再弹栈一次往右侧找
class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        stack, res = [], []
        node = root
        while node or stack:
            while node: # 一路向左
                stack.append(node)
                res.append(node.val)
                node = node.left
            node = stack.pop()
            node = node.right
        return res

中序遍历
和前序遍历套路差不多, 都有先一路向左的风范, 不同的是, 在一路向左的过程中, 没有记录结果, 最后弹栈一次并记录(形象一点就是深入浅出), 取道向右作为新的root

class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        stack, res = [], []
        node = root
        while node or stack: # 这里两个条件比较巧妙, 也可以初始化为stack = [root]
            while node: # 一路向左
                stack.append(node)
                node = node.left
            node = stack.pop()
            res.append(node.val) # 每次弹栈记录到res中
            node = node.right # if right==None, stop
        return res

后续遍历

posted @ 2022-08-21 17:21  别把梦弄脏  阅读(221)  评论(0)    收藏  举报