LeetCode 102:二叉树的层序遍历(BFS 与 DFS 两种解法)

11 阅读3分钟

二叉树的**层序遍历(Level Order Traversal)**是非常经典的一道题,几乎是所有树题的“入门必刷”。

这道题的价值不在于“会不会写”,而在于:

  • 你是否真正理解「一层一层」是什么意思
  • 你是否知道:层序遍历不仅能用 BFS,也能用 DFS 写

本文将用两种方式完整讲解:

  1. BFS(队列,最常规做法)
  2. DFS(递归 + 层号,进阶思路)

一、题目要求

给定一棵二叉树,返回其节点值的层序遍历结果

也就是说:

  • 第一层放在第一个 List
  • 第二层放在第二个 List
  • 以此类推

返回结构如下:

[  [第一层节点],
  [第二层节点],
  [第三层节点],
  ...
]

二、方法一:BFS(队列,最标准解法)

1. 思路分析

层序遍历的本质就是:

  • 先访问根节点
  • 再访问根节点的所有子节点
  • 再访问下一层的所有节点

这个“先进先出”的访问顺序,天然适合用队列(Queue)

核心思想只有一句话:

每一轮循环,处理队列中“当前层”的所有节点


2. 完整代码(BFS)

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();

        if (root == null) {
            return res;
        }

        queue.offer(root);

        while (!queue.isEmpty()) {
            int levelSize = queue.size();
            List<Integer> currentLevel = new ArrayList<>();

            for (int i = 0; i < levelSize; i++) {
                TreeNode node = queue.poll();
                currentLevel.add(node.val);

                if (node.left != null) {
                    queue.offer(node.left);
                }

                if (node.right != null) {
                    queue.offer(node.right);
                }
            }

            res.add(currentLevel);
        }

        return res;
    }
}

3. 逐行关键解释

queue.offer(root);

先把根节点放进队列,作为第一层的起点。

int levelSize = queue.size();

这一句是层序遍历的灵魂

  • 队列里现在有多少个节点
  • 就说明这一层有多少个节点
for (int i = 0; i < levelSize; i++)

这一层的节点,必须一次性处理完,才能保证分层正确。

queue.offer(node.left);
queue.offer(node.right);

在处理当前层的同时,把下一层的节点提前放进队列。


4. BFS 的特点

  • 思路直观
  • 实现简单
  • 是层序遍历的“标准答案”

也是面试中最推荐优先写的版本


三、方法二:DFS(递归 + 层号)

很多人第一次看到这道题会以为:

层序遍历只能用 BFS

但实际上,只要我们在 DFS 时记录当前层数,也可以做到完全一样的效果。


1. 思路分析

DFS 的关键点有两个:

  1. 每往下一层递归,层号 level + 1
  2. 如果当前层的 List 不存在,就先创建

本质是:

用递归的“深度”,模拟“层”的概念


2. 完整代码(DFS)

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(root, res, 0);
        return res;
    }

    private void dfs(TreeNode root, List<List<Integer>> res, int level) {
        if (root == null) {
            return;
        }

        if (res.size() == level) {
            res.add(new ArrayList<>());
        }

        res.get(level).add(root.val);

        dfs(root.left, res, level + 1);
        dfs(root.right, res, level + 1);
    }
}

3. 逐行关键解释

dfs(root, res, 0);

从根节点开始,层号从 0 开始。

if (res.size() == level) {
    res.add(new ArrayList<>());
}

说明这是第一次到达这一层,需要创建对应的 List。

res.get(level).add(root.val);

当前节点,放入它所属的那一层。

dfs(root.left, res, level + 1);
dfs(root.right, res, level + 1);

递归进入下一层,层号加一。


4. DFS 的特点

  • 写法优雅
  • 思路巧妙
  • 非常适合理解「DFS + 层信息」这类题型

但在面试中,可读性略逊于 BFS,通常作为加分解法。


四、BFS vs DFS 对比

维度BFS(队列)DFS(递归)
是否直观非常直观需要理解层号
是否符合直觉稍微绕一点
实现难度
面试推荐度

建议刷题顺序:

  1. 先掌握 BFS 版本
  2. 再补充 DFS 版本,提升抽象能力