来源:go-interview / Algorithm

树的遍历是指按一定顺序访问树中所有节点的过程。常见的树遍历方式主要有以下几种:

1. 前序遍历(Pre-order Traversal)

访问顺序:根节点 → 左子树 → 右子树

特点:

  • 先访问根节点,再递归遍历左子树和右子树。
  • 可以用来复制树结构。

递归实现:

func preOrderTraversal(root *TreeNode) {
    if root == nil {
        return
    }
    fmt.Println(root.Val) // 访问根节点
    preOrderTraversal(root.Left)  // 递归遍历左子树
    preOrderTraversal(root.Right) // 递归遍历右子树
}

非递归实现:

func preOrderTraversal(root *TreeNode) {
    if root == nil {
        return
    }
    stack := []*TreeNode{root}
    for len(stack) > 0 {
        node := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        fmt.Println(node.Val) // 访问根节点
        if node.Right != nil {
            stack = append(stack, node.Right) // 先入栈右子树
        }
        if node.Left != nil {
            stack = append(stack, node.Left) // 后入栈左子树
        }
    }
}

2. 中序遍历(In-order Traversal)

访问顺序:左子树 → 根节点 → 右子树

特点:

  • 先递归遍历左子树,再访问根节点,最后递归遍历右子树。
  • 中序遍历二叉搜索树时,会得到一个有序的数列。

递归实现:

func inOrderTraversal(root *TreeNode) {
    if root == nil {
        return
    }
    inOrderTraversal(root.Left)   // 递归遍历左子树
    fmt.Println(root.Val)          // 访问根节点
    inOrderTraversal(root.Right)  // 递归遍历右子树
}

非递归实现:

func inOrderTraversal(root *TreeNode) {
    stack := []*TreeNode{}
    current := root
    for current != nil || len(stack) > 0 {
        for current != nil {
            stack = append(stack, current)
            current = current.Left
        }
        current = stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        fmt.Println(current.Val) // 访问根节点
        current = current.Right
    }
}

3. 后序遍历(Post-order Traversal)

访问顺序:左子树 → 右子树 → 根节点

特点:

  • 先递归遍历左子树,再递归遍历右子树,最后访问根节点。
  • 常用于删除树,确保在删除父节点前删除子节点。

递归实现:

func postOrderTraversal(root *TreeNode) {
    if root == nil {
        return
    }
    postOrderTraversal(root.Left)  // 递归遍历左子树
    postOrderTraversal(root.Right) // 递归遍历右子树
    fmt.Println(root.Val)           // 访问根节点
}

非递归实现:

func postOrderTraversal(root *TreeNode) {
    if root == nil {
        return
    }
    stack1 := []*TreeNode{root}
    stack2 := []*TreeNode{}
    for len(stack1) > 0 {
        node := stack1[len(stack1)-1]
        stack1 = stack1[:len(stack1)-1]
        stack2 = append(stack2, node)
        if node.Left != nil {
            stack1 = append(stack1, node.Left) // 先入栈左子树
        }
        if node.Right != nil {
            stack1 = append(stack1, node.Right) // 后入栈右子树
        }
    }
    for len(stack2) > 0 {
        node := stack2[len(stack2)-1]
        stack2 = stack2[:len(stack2)-1]
        fmt.Println(node.Val) // 访问根节点
    }
}

4. 层次遍历(Level-order Traversal)

访问顺序:逐层访问,每层从左到右

特点:

  • 层次遍历通常使用队列来实现。
  • 常用于查找树的最短路径、最宽层等。

实现:

func levelOrderTraversal(root *TreeNode) {
    if root == nil {
        return
    }
    queue := []*TreeNode{root}
    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        fmt.Println(node.Val) // 访问根节点
        if node.Left != nil {
            queue = append(queue, node.Left) // 入队左子树
        }
        if node.Right != nil {
            queue = append(queue, node.Right) // 入队右子树
        }
    }
}

总结

  • 前序遍历:用于前置操作,如复制树、表达式树的输出。
  • 中序遍历:特别适合二叉搜索树,用于得到有序序列。
  • 后序遍历:用于后置操作,如删除节点。
  • 层次遍历:常用于查找问题,逐层扫描节点。