二叉树的**层序遍历(Level Order Traversal)**是非常经典的一道题,几乎是所有树题的“入门必刷”。
这道题的价值不在于“会不会写”,而在于:
- 你是否真正理解「一层一层」是什么意思
- 你是否知道:层序遍历不仅能用 BFS,也能用 DFS 写
本文将用两种方式完整讲解:
- BFS(队列,最常规做法)
- 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 的关键点有两个:
- 每往下一层递归,层号
level + 1 - 如果当前层的 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(递归) |
|---|---|---|
| 是否直观 | 非常直观 | 需要理解层号 |
| 是否符合直觉 | 是 | 稍微绕一点 |
| 实现难度 | 低 | 中 |
| 面试推荐度 | 高 | 中 |
建议刷题顺序:
- 先掌握 BFS 版本
- 再补充 DFS 版本,提升抽象能力