目录

二叉树遍历详解:前序、中序、后序、层序 4 种方式图文讲解与 Go 实现

概述

二叉树遍历是数据结构与算法领域的基础知识点,也是各大厂面试中的高频考题。简单来说,遍历就是按照某种特定顺序,依次访问二叉树中的每一个节点,且每个节点恰好被访问一次

根据访问顺序的不同,二叉树遍历主要分为以下 4 种方式:

遍历方式 访问顺序 搜索策略
前序遍历 根 → 左 → 右 深度优先(DFS)
中序遍历 左 → 根 → 右 深度优先(DFS)
后序遍历 左 → 右 → 根 深度优先(DFS)
层序遍历 逐层从左到右 广度优先(BFS)

下面我们以这棵二叉树为例,逐一讲解每种遍历方式的原理和记忆方法:

https://i-blog.csdnimg.cn/blog_migrate/448449e6e55ef2e29f18a76a5cda9c89.png


前序遍历(Pre-order)

访问规则

前序遍历的口诀很简单:根 → 左 → 右。也就是说,每到一个节点,先记录当前节点的值,然后递归遍历左子树,最后递归遍历右子树。

图解记忆法

你可以想象有一个小人,从根节点出发,沿着二叉树的外沿逆时针走一圈,最终回到根节点。小人在路途中第一次经过某个节点时,就把该节点的值记下来,按顺序排列出来就是前序遍历的结果。

以上图的二叉树为例,前序遍历结果为:A B D H I E J C F K G

https://i-blog.csdnimg.cn/blog_migrate/213daf6b3536931668097917206a4a2f.png

Go 语言实现

递归写法:

// TreeNode 定义二叉树节点
type TreeNode struct {
    Val   string
    Left  *TreeNode
    Right *TreeNode
}

// PreorderTraversal 前序遍历(递归)
func PreorderTraversal(root *TreeNode) []string {
    if root == nil {
        return nil
    }
    result := []string{root.Val}
    result = append(result, PreorderTraversal(root.Left)...)
    result = append(result, PreorderTraversal(root.Right)...)
    return result
}

迭代写法(利用栈):

// PreorderIterative 前序遍历(迭代)
func PreorderIterative(root *TreeNode) []string {
    if root == nil {
        return nil
    }
    var result []string
    stack := []*TreeNode{root}

    for len(stack) > 0 {
        // 弹出栈顶
        node := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        result = append(result, node.Val)

        // 注意:先压右子节点,再压左子节点
        // 这样出栈时才是先左后右
        if node.Right != nil {
            stack = append(stack, node.Right)
        }
        if node.Left != nil {
            stack = append(stack, node.Left)
        }
    }
    return result
}

迭代写法的核心思路是用一个栈来模拟递归过程。由于栈是后进先出的结构,所以要先把右子节点压入栈中,再压左子节点,这样弹出时的顺序才符合"先左后右"的要求。


中序遍历(In-order)

访问规则

中序遍历的口诀是:左 → 根 → 右。即先递归遍历左子树,再记录当前节点,最后递归遍历右子树。

图解记忆法

中序遍历有一个非常直观的理解方式:把二叉树的每个节点垂直向下投影到一条水平线上,就像每个节点受到重力作用掉落到地面一样。然后从左到右依次读取地面上节点的值,得到的就是中序遍历的结果。

以上图的二叉树为例,中序遍历结果为:H D I B J E A F K C G

https://i-blog.csdnimg.cn/blog_migrate/28ce5d2d7e2cfeb1133389fa2f7c20b5.gif

Go 语言实现

递归写法:

// InorderTraversal 中序遍历(递归)
func InorderTraversal(root *TreeNode) []string {
    if root == nil {
        return nil
    }
    var result []string
    result = append(result, InorderTraversal(root.Left)...)
    result = append(result, root.Val)
    result = append(result, InorderTraversal(root.Right)...)
    return result
}

迭代写法(利用栈):

// InorderIterative 中序遍历(迭代)
func InorderIterative(root *TreeNode) []string {
    var result []string
    var stack []*TreeNode
    curr := root

    for curr != nil || len(stack) > 0 {
        // 一路向左,把沿途的节点全部压入栈
        for curr != nil {
            stack = append(stack, curr)
            curr = curr.Left
        }
        // 弹出栈顶节点并记录值
        curr = stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        result = append(result, curr.Val)
        // 转向右子树
        curr = curr.Right
    }
    return result
}

中序遍历的迭代写法稍微复杂一些。核心思路是:先沿着左子树一直往下走,把经过的节点依次压栈;走到头后弹出栈顶节点处理;然后转向该节点的右子树,重复这个过程。

补充知识:对于二叉搜索树(BST)来说,中序遍历的结果恰好是一个有序序列,这个性质在很多算法题中非常有用。


后序遍历(Post-order)

访问规则

后序遍历的口诀是:左 → 右 → 根。即先递归遍历左子树,再递归遍历右子树,最后才记录当前节点。所以根节点一定是后序遍历结果中的最后一个元素。

图解记忆法

后序遍历可以用 “剪葡萄” 的方式来记忆。想象你面前有一串葡萄挂在树上,你需要把葡萄一颗一颗地摘下来——每次只能摘掉一颗处于末端的、一剪刀就能剪下来的葡萄。你沿着树的外围转一圈,看到能直接摘下来的就摘掉,按摘下的先后顺序排列,就是后序遍历的结果。

以上图的二叉树为例,后序遍历结果为:H I D J E B K F G C A

https://i-blog.csdnimg.cn/blog_migrate/b3dad70387916c5d61dc8fdebe63599c.gif

Go 语言实现

递归写法:

// PostorderTraversal 后序遍历(递归)
func PostorderTraversal(root *TreeNode) []string {
    if root == nil {
        return nil
    }
    var result []string
    result = append(result, PostorderTraversal(root.Left)...)
    result = append(result, PostorderTraversal(root.Right)...)
    result = append(result, root.Val)
    return result
}

迭代写法(逆序技巧):

// PostorderIterative 后序遍历(迭代)
func PostorderIterative(root *TreeNode) []string {
    if root == nil {
        return nil
    }
    var result []string
    stack := []*TreeNode{root}

    for len(stack) > 0 {
        node := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        // 把值插入结果切片的最前面
        result = append([]string{node.Val}, result...)

        // 先压左再压右(和前序相反)
        if node.Left != nil {
            stack = append(stack, node.Left)
        }
        if node.Right != nil {
            stack = append(stack, node.Right)
        }
    }
    return result
}

后序遍历的迭代写法有一个巧妙的思路:前序遍历的顺序是"根 → 左 → 右",如果我们把它改成"根 → 右 → 左",再将整个结果逆序,就得到了"左 → 右 → 根"——也就是后序遍历。上面的代码正是利用了这个技巧。


层序遍历(Level-order)

访问规则

层序遍历是唯一一种基于广度优先搜索(BFS)的遍历方式。规则非常直接:从根节点开始,从上到下逐层访问,同一层内从左到右依次读取

以上图的二叉树为例,层序遍历结果为:A B C D E F G H I J K

https://i-blog.csdnimg.cn/blog_migrate/bbd78ab9fba7261fd046da3175b10395.png

Go 语言实现

层序遍历通常使用队列来实现:

// LevelOrderTraversal 层序遍历
func LevelOrderTraversal(root *TreeNode) []string {
    if root == nil {
        return nil
    }
    var result []string
    queue := []*TreeNode{root}

    for len(queue) > 0 {
        // 取出队首节点
        node := queue[0]
        queue = queue[1:]
        result = append(result, node.Val)

        // 依次将左、右子节点加入队列
        if node.Left != nil {
            queue = append(queue, node.Left)
        }
        if node.Right != nil {
            queue = append(queue, node.Right)
        }
    }
    return result
}

层序遍历的实现核心是队列(先进先出)。每次从队列头部取出一个节点,处理后把它的左右子节点加入队尾。这样就自然保证了逐层、从左到右的访问顺序。


4 种遍历方式对比总结

对比项 前序遍历 中序遍历 后序遍历 层序遍历
访问顺序 根 → 左 → 右 左 → 根 → 右 左 → 右 → 根 逐层从左到右
搜索策略 DFS DFS DFS BFS
递归实现难度 简单 简单 简单 不适用
迭代实现难度 简单 中等 中等 简单
辅助数据结构 队列
典型应用场景 复制二叉树、序列化 BST 有序输出 释放内存、计算表达式树 按层打印、最短路径

常见问题

1. 前序、中序、后序遍历名字中的"序"指的是什么?

这里的"序"指的是根节点的访问时机。前序 = 根节点在最前面访问;中序 = 根节点在中间访问;后序 = 根节点在最后访问。左子树始终在右子树之前被访问,不同的只是根节点出现的位置。

2. 递归和迭代写法哪个更好?

递归写法代码简洁,逻辑清晰,非常适合理解遍历的本质。但在实际工程中,如果树的深度非常大,递归可能会导致栈溢出(stack overflow)。迭代写法虽然代码更长,但可以手动控制栈的大小,更加安全。面试中两种写法最好都能熟练掌握。

3. 如何根据遍历结果还原一棵二叉树?

单独的一种遍历结果无法唯一确定一棵二叉树。但有两种经典组合可以还原:

  • 前序 + 中序 → 可以唯一确定
  • 后序 + 中序 → 可以唯一确定

注意,前序 + 后序的组合在一般情况下不能唯一确定二叉树。

4. 层序遍历有哪些实际应用?

层序遍历在实际开发中使用广泛,例如:按层打印二叉树、计算二叉树的最大/最小深度、判断完全二叉树、寻找二叉树中每层的最大值等。很多 LeetCode 题目都是基于层序遍历的变形。

5. DFS 和 BFS 有什么区别?

DFS(深度优先搜索)会尽可能深地探索分支,直到走不下去才回溯。前序、中序、后序遍历都属于 DFS。BFS(广度优先搜索)则是逐层展开,层序遍历就是典型的 BFS。两者适用场景不同,DFS 适合搜索路径相关问题,BFS 适合最短路径和按层处理的问题。

总结

二叉树的遍历是学习树结构和算法的第一步,也是各类编程面试中绕不开的考点。本文介绍了 4 种遍历方式——前序、中序、后序和层序遍历,每种都给出了直观的图解记忆方法,并提供了 Go 语言的递归与迭代实现代码。

掌握这 4 种遍历的关键在于理解它们的核心区别:

  • 前序、中序、后序 的区别仅在于根节点的访问时机
  • 层序遍历 使用队列而非栈,体现了广度优先的思路
  • 递归写法 帮你理解原理,迭代写法 帮你应对工程实践

建议你先把每种遍历的递归写法彻底理解透,再去攻克迭代写法。遇到相关的算法题时,先判断属于哪种遍历的变形,再套用对应的模板,很多问题就能迎刃而解了。

如果大家对二叉树遍历还有任何疑问,或者遇到了什么棘手的遍历问题,欢迎在评论区交流讨论~~~

版权声明

未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!

本文原文链接: https://fiveyoboy.com/articles/binary-tree-traversal/

备用原文链接: https://blog.fiveyoboy.com/articles/binary-tree-traversal/