【LeetCode 二叉树专项】二叉树的中序遍历(94)

1. 题目

给定一个二叉树,返回它的 中序 遍历。

1.1 示例

  • 示例 1 1 1
  • 输入: [1, null, 2, 3]
  • 输出: [1, 3, 2]

在这里插入图片描述

1.2 说明

1.3 进阶

递归算法很简单,你可以通过迭代算法完成吗?

2. 解法一(递归法)

2.1 分析

类似【LeetCode 二叉树专项】二叉树的前序遍历(144),二叉树的中序遍历使用递归的方式很容易实现,即按照访问 左子树 -> 根节点 -> 右子树 的方式遍历这棵树,而在访问左子树或者右子树的时候,按照同样的方式遍历,直到遍历完整棵树。可以看出,整个遍历过程天然具有递归的性质,因此可以直接用递归函数来模拟这一过程。

2.2 解答

from typing import List


class TreeNode:
    def __init__(self, val=0, left: 'TreeNode' = None, right: 'TreeNode' = None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    def __init__(self):
        self.tree = []

    def _inorder(self, root: TreeNode):
        if not root:
            return
        self._inorder(root.left)
        self.tree.append(root.val)
        self._inorder(root.right)

    def postorder_traversal(self, root: TreeNode) -> List[int]:
        self._inorder(root)
        return self.tree

2.3 复杂度

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的节点数。每一个节点恰好被遍历一次。
  • 空间复杂度: O ( n ) O(n) O(n) ,为递归过程中递归栈的开销和实例属性 self.tree 所占用空间两者组成,其中前者平均情况下为 O ( log ⁡ n ) O(\log n) O(logn) ,最坏情况下树呈现链状,为 O ( n ) O(n) O(n)

3. 解法二(迭代法)

3.1 分析

虽然迭代形式的二叉树中序遍历仍然借用栈作为辅助的数据结构,但是要比前序遍历的迭代形式复杂一些,实现迭代形式的中序遍历主要步骤如下:

  • 步骤一: 创建一个空的栈 stack
  • 步骤二: 初始化游标变量 cursor 使其引用 root
  • 步骤三:cursor 引用的节点压栈并且让 cursor 指向 cursor.left ,重复此操作直到 cursor 引用 None
  • 步骤四: 如果 stack 非空且 cursor 引用 None ,则重复下列步骤:
    • (a):从 stack 栈顶弹出节点并由 node 引用;
    • (b):执行对弹出节点的遍历操作;
    • (c):使得cursor 引用 node.right
    • (d):重复步骤三。
  • 步骤五: 如果 cursor 引用 Nonestack 为空,则中序遍历完成。

为便于理解,对于形如下列形式的二叉树,使用上述迭代步骤的具体情况如下:

		            1
		          /   \
		        2      3
		      /  \
		    4     5
  • 步骤一: 创建一个空的栈 stackstack = []
  • 步骤二: 初始化游标变量 cursor 使其引用 rootcursor -> 1
  • 步骤三:cursor 引用的节点压栈并且让 cursor 指向 cursor.left ,重复此操作直到 cursor 引用 None
    • cursor -> 1
    • 1 压栈: stack = [1]
    • cursor -> 2
    • 2 压栈: stack = [1, 2]
    • cursor -> 4
    • 4 压栈: stack = [1, 2, 4]
    • cursor = None
  • 步骤四: stack 非空且 cursor 引用 None ,重复下列步骤:
    • (a):从 stack 栈顶弹出节点: stack = [1, 2]
    • (b):执行对于 4 对应节点的遍历操作;
    • (c):使得 cursor 引用 4 对应节点的右子节点,即 cursor = None
    • (d):由于 cursor = None ,步骤三不会做任何操作。

    • (a):从 stack 栈顶弹出节点: stack = [1]
    • (b):执行对于 2 对应节点的遍历操作;
    • (c):使得 cursor 引用 2 对应节点的右子节点,即 cursor -> 5
    • (d):此时 cursor -> 5 ,执行步骤三。
  • 步骤三:cursor 引用的节点压栈并且让 cursor 指向 cursor.left ,重复此操作直到 cursor 引用 None
    • cursor -> 5
    • 5 压栈: stack = [1, 5]
    • cursor = None
  • 步骤四: stack 非空且 cursor 引用 None ,重复下列步骤:
    • (a):从 stack 栈顶弹出节点: stack = [1]
    • (b):执行对于 5 对应节点的遍历操作;
    • (c):使得 cursor 引用 5 对应节点的右子节点,即 cursor = None
    • (d):由于 cursor = None ,步骤三不会做任何操作。

    • (a):从 stack 栈顶弹出节点: stack = []
    • (b):执行对于 1 对应节点的遍历操作;
    • (c):使得 cursor 引用 1 对应节点的右子节点,即 cursor -> 3
    • (d):此时 cursor -> 3 ,执行步骤三。
  • 步骤三:cursor 引用的节点压栈并且让 cursor 指向 cursor.left ,重复此操作直到 cursor 引用 None
    • cursor -> 3
    • 3 压栈: stack = [3]
    • cursor = None
  • 步骤四: stack 非空且 cursor 引用 None ,重复下列步骤:
    • (a):从 stack 栈顶弹出节点: stack = []
    • (b):执行对于 3 对应节点的遍历操作;
    • (c):使得 cursor 引用 3 对应节点的右子节点,即 cursor = None
    • (d):由于 cursor = None ,步骤三不会做任何操作。
  • 步骤五: 最终, stack 为空且 cursor 引用 None ,中序遍历完成。

3.2 解答

根据上述分析,得到二叉树中序遍历的迭代形式如下:

from typing import List


class TreeNode:
    def __init__(self, val=0, left: 'TreeNode' = None, right: 'TreeNode' = None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    def __init__(self):
        self.tree = []

    def iterative_inorder(self, root: TreeNode) -> List[int]:
        if not root:
            return self.tree
        stack = []
        cursor = root
        while cursor:
            stack.append(cursor)
            cursor = cursor.left
        while stack:
            node = stack.pop()
            self.tree.append(node.val)
            cursor = node.right
            while cursor:
                stack.append(cursor)
                cursor = cursor.left
        return self.tree

实际上,上述代码在实现步骤三时有重复的代码实现,可进一步简化为如下代码:

from typing import List


class TreeNode:
    def __init__(self, val=0, left: 'TreeNode' = None, right: 'TreeNode' = None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    def __init__(self):
        self.tree = []

    def succinct_iterative_inorder(self, root: TreeNode) -> List[int]:
        if not root:
            return self.tree
        stack = []
        cursor = root
        while True:
            if cursor:
                stack.append(cursor)
                cursor = cursor.left
            elif stack:
                node = stack.pop()
                self.tree.append(node.val)
                cursor = node.right
            else:
                break
        return self.tree


def main():
    """ Constructed binary tree is
                1
              /   \
             2     3
           /  \
          4    5
    """
    root = TreeNode(1)
    root.left = TreeNode(2)
    root.right = TreeNode(3)
    root.left.left = TreeNode(4)
    root.left.right = TreeNode(5)
    sln = Solution()
    print(sln.succinct_iterative_inorder(root))  # [4, 2, 5, 1, 3]


if __name__ == '__main__':
    main()

3.3 复杂度

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
  • 空间复杂度: O ( n ) O(n) O(n)。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O ( n ) O(n) O(n) 的级别。

4. 解法三(Morris 遍历)

猜你喜欢

转载自blog.csdn.net/weixin_37780776/article/details/121194455