返回

二叉树遍历:以“黑白标记法”为切入点,深入理解前中后序

后端

前言

作为一名技术爱好者,您是否遇到过这样的困扰:需要遍历二叉树,但却被前中后序遍历的递归法和迭代法搞得晕头转向?别担心,本文将为您拨开迷雾,带您深入理解这三种遍历方法,并介绍一种简单易记的迭代法模版——“黑白标记法”。

理解二叉树遍历

二叉树遍历是计算机科学和编程中的一个基本算法,它用于访问二叉树中的所有节点。二叉树遍历有三种基本方式:前序遍历、中序遍历和后序遍历。

  • 前序遍历: 根节点、左子树、右子树
  • 中序遍历: 左子树、根节点、右子树
  • 后序遍历: 左子树、右子树、根节点

递归法:简单易懂,但有局限性

递归法是实现二叉树遍历最直接的方法。它的原理很简单:

  1. 访问根节点。
  2. 递归地遍历左子树。
  3. 递归地遍历右子树。
def preorder_traversal(root):
    if root is None:
        return

    # 访问根节点
    print(root.data)

    # 递归地遍历左子树
    preorder_traversal(root.left)

    # 递归地遍历右子树
    preorder_traversal(root.right)


def inorder_traversal(root):
    if root is None:
        return

    # 递归地遍历左子树
    inorder_traversal(root.left)

    # 访问根节点
    print(root.data)

    # 递归地遍历右子树
    inorder_traversal(root.right)


def postorder_traversal(root):
    if root is None:
        return

    # 递归地遍历左子树
    postorder_traversal(root.left)

    # 递归地遍历右子树
    postorder_traversal(root.right)

    # 访问根节点
    print(root.data)

递归法简单易懂,但也有局限性。当二叉树非常庞大的时候,递归法会消耗大量的栈空间,甚至导致栈溢出。

迭代法:空间复杂度更低,效率更高

迭代法是实现二叉树遍历的另一种方法。它的原理也很简单:

  1. 使用栈来存储要访问的节点。
  2. 将根节点压入栈中。
  3. 循环执行以下步骤,直到栈为空:
    • 弹出栈顶元素,并访问它。
    • 如果它有右子树,则将右子树压入栈中。
    • 如果它有左子树,则将左子树压入栈中。
def preorder_traversal_iterative(root):
    stack = [root]

    while stack:
        # 弹出栈顶元素,并访问它
        node = stack.pop()
        print(node.data)

        # 如果它有右子树,则将右子树压入栈中
        if node.right:
            stack.append(node.right)

        # 如果它有左子树,则将左子树压入栈中
        if node.left:
            stack.append(node.left)


def inorder_traversal_iterative(root):
    stack = []
    node = root

    while stack or node:
        # 如果当前节点不为空,则将其压入栈中并继续访问其左子树
        if node:
            stack.append(node)
            node = node.left

        # 如果当前节点为空,则弹出栈顶元素并访问它,然后访问其右子树
        else:
            node = stack.pop()
            print(node.data)
            node = node.right


def postorder_traversal_iterative(root):
    stack = []
    last_visited = None

    while stack or root:
        # 如果当前节点不为空,则将其压入栈中并继续访问其左子树
        if root:
            stack.append(root)
            root = root.left

        # 如果当前节点为空,则说明左子树已经访问完毕,此时访问右子树
        else:
            root = stack[-1]

            # 如果右子树已经访问完毕,则弹出栈顶元素并访问它
            if not root.right or last_visited == root.right:
                root = stack.pop()
                print(root.data)
                last_visited = root
                root = None

            # 否则,访问右子树
            else:
                root = root.right

迭代法的时间复杂度与递归法相同,但空间复杂度更低。因此,当二叉树非常庞大的时候,迭代法是更好的选择。

黑白标记法:简单易记的迭代法模版

“黑白标记法”是一种实现二叉树遍历的迭代法模版。它的原理很简单:

  1. 将所有节点标记为“白色”。
  2. 从根节点开始,访问它并将其标记为“黑色”。
  3. 循环执行以下步骤,直到所有节点都被标记为“黑色”:
    • 找到一个标记为“白色”的节点,将其标记为“灰色”。
    • 访问该节点。
    • 将其左子树和右子树标记为“白色”。
def tree_traversal_黑白标记法(root, traversal_type):
    # 将所有节点标记为“白色”
    for node in tree.nodes:
        node.color = "white"

    # 从根节点开始,访问它并将其标记为“黑色”
    root.color = "black"

    # 循环执行以下步骤,直到所有节点都被标记为“黑色”
    while True:
        # 找到一个标记为“白色”的节点,将其标记为“灰色”
        node = find_white_node(tree)
        if node is None:
            break
        node.color = "gray"

        # 访问该节点
        if traversal_type == "preorder":
            print(node.data)
        elif traversal_type == "inorder":
            # 如果该节点的左子树已经访问完毕,则访问该节点
            if node.left.color == "black":
                print(node.data)

            # 否则,访问该节点的左子树
            else:
                node = node.left
        elif traversal_type == "postorder":
            # 如果该节点的左右子树都已经访问完毕,则访问该节点
            if node.left.color == "black" and node.right.color == "black":
                print(node.data)

            # 否则,访问该节点的左子树或右子树
            else:
                if node.left.color == "white":
                    node = node.left
                else:
                    node = node.right

        # 将其左子树和右子树标记为“白色”
        node.left.color = "white"
        node.right.color = "white"

“黑白标记法”简单易记,并且可以用于实现任何类型的二叉树遍历。

结语

本文介绍了二叉树的前中后序遍历算法,并重点介绍了一种简单易记的迭代法模版——“黑白标记法”。希望这些知识能够帮助您更好地理解二叉树遍历算法,并在实际开发中熟练运用它们。