二叉树遍历

32 阅读3分钟

二叉树遍历

二叉树常见的遍历方式包括层序遍历、前序遍历、中序遍历和后序遍历等

层序遍历

层序遍历 从顶部到底部逐层遍历二叉树 并在每一层按照从左到右的顺序访问节点

层序遍历本质上属于广度优先遍历,也称广度优先搜索(BFS),它体现了一种“一圈一圈向外扩展”的逐层遍历方式

广度优先遍历通常借助“队列”来实现。队列遵循“先进先出”的规则,而广度优先遍历则遵循“逐层推进”的规则,两者背后的思想是一致的

func levelOrder(root *TreeNode) []any {
    // 1. 初始化队列容器(使用双向链表实现)
    //    示例:初始时队列为空 []
    queue := list.New()
    // 2. 将根节点加入队列尾部(如果是空树则加入nil)
    //    示例:当root=1节点,队列变为[1]
    queue.PushBack(root)
    // 3. 初始化结果切片(长度0的动态数组)
    //    示例:初始nums=[]
    nums := make([]any, 0)
    // 4. 主循环:当队列不为空时持续遍历
    //    示例:初始队列非空(root=1时),进入循环
    for queue.Len() > 0 {
        // 5. 出队操作:
        //    a. Front()获取队列首元素(链表节点)
        //    b. Remove()移除该节点并返回值
        //    c. 类型断言(*TreeNode)转为树节点
        //    示例:取出根节点1,队列变空[]
        node := queue.Remove(queue.Front()).(*TreeNode)
        // 6. 将当前节点值加入结果集
        //    示例:nums从[]变为[1]
        nums = append(nums, node.Val)
        // 7. 处理左子节点:
        //    示例:当node=1时,左子节点2存在 → 队列变为[2]
        if node.Left != nil {
            queue.PushBack(node.Left) // 加入队列尾部
        }
        // 8. 处理右子节点:
        //    示例:当node=1时,右子节点3存在 → 队列变为[2,3]
        if node.Right != nil {
            queue.PushBack(node.Right) // 加入队列尾部(左节点之后)
        }
    }
    // 9. 返回层级遍历结果
    //    示例二叉树:
    //        1
    //       / \
    //      2   3
    //     / \
    //    4   5
    //    最终返回 [1,2,3,4,5]
    return nums
}
​
树结构:
    1
   / \
  2   3
 / \   \
4   5   6
循环次数当前出队节点操作后队列状态子节点入队结果集变化
11[2,3]左2→右3[1]→[1]
22[3,4,5]左4→右5[1]→[1,2]
33[4,5,6]右6[1,2]→[1,2,3]
44[5,6]无子节点[1,2,3]→[1,2,3,4]
55[6]无子节点...→[1,2,3,4,5]
66[]无子节点...→[1,2,3,4,5,6]

前序、中序、后序遍历

前序、中序和后序遍历都属于深度优先遍历,也称深度优先搜索(DFS),它体现了一种“先走到尽头,再回溯继续”的遍历方式

深度优先遍历就像是绕着整棵二叉树的外围“走”一圈,在每个节点都会遇到三个位置,分别对应前序遍历、中序遍历和后序遍历

深度优先搜索通常基于递归实现

/* 前序遍历 */
func preOrder(node *TreeNode) {
    // 基线条件:遇到空节点(叶子节点的子节点)则返回
    // 示例:遍历到节点2的左子节点(nil)时直接返回
    if node == nil {
        return
    }
    // 访问优先级:根节点 -> 左子树 -> 右子树
    // 示例:对于根节点1 -> 节点2 -> 节点4的访问顺序
    nums = append(nums, node.Val)    // 先记录当前节点值
    preOrder(node.Left)              // 递归遍历左子树
    preOrder(node.Right)             // 递归遍历右子树
}
​
/* 中序遍历 */
func inOrder(node *TreeNode) {
    // 基线条件:空节点直接返回
    // 示例:节点4的左子节点(nil)直接返回
    if node == nil {
        return
    }
    // 访问优先级:左子树 -> 根节点 -> 右子树
    // 示例:对于节点2 -> 节点4 -> 节点5 -> 节点1 -> 节点3的访问顺序
    inOrder(node.Left)               // 先递归遍历左子树
    nums = append(nums, node.Val)    // 然后记录当前节点值
    inOrder(node.Right)              // 最后递归遍历右子树
}
​
/* 后序遍历 */
func postOrder(node *TreeNode) {
    // 基线条件:空节点直接返回
    // 示例:节点5的右子节点(nil)直接返回
    if node == nil {
        return
    }
    // 访问优先级:左子树 -> 右子树 -> 根节点
    // 示例:对于节点4 -> 节点5 -> 节点2 -> 节点6 -> 节点3 -> 节点1的访问顺序
    postOrder(node.Left)             // 先递归遍历左子树
    postOrder(node.Right)            // 然后递归遍历右子树
    nums = append(nums, node.Val)    // 最后记录当前节点值
}