给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
这是二叉树的层序遍历问题,按层从上到下、从左到右遍历二叉树。
核心思路
BFS(广度优先搜索)+ 队列:逐层遍历,每次处理完一层的所有节点后再处理下一层。
二叉树:
3
/ \
9 20
/ \
15 7
层序遍历:
[[3], [9,20], [15,7]]
解法1:BFS(推荐)
代码
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
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);
}
}
result.add(currentLevel);
}
return result;
}
}
```
### 详细演示
```
二叉树:
3
/ \
9 20
/ \
15 7
初始:
queue = [3]
result = []
第1层:
------------------
levelSize = 1
currentLevel = []
i=0:
poll: node=3
add: 3 → currentLevel=[3]
offer: 9, 20
queue = [9, 20]
result.add([3])
result = [[3]]
第2层:
------------------
levelSize = 2
currentLevel = []
i=0:
poll: node=9
add: 9 → currentLevel=[9]
无子节点
queue = [20]
i=1:
poll: node=20
add: 20 → currentLevel=[9, 20]
offer: 15, 7
queue = [15, 7]
result.add([9, 20])
result = [[3], [9, 20]]
第3层:
------------------
levelSize = 2
currentLevel = []
i=0:
poll: node=15
add: 15 → currentLevel=[15]
无子节点
queue = [7]
i=1:
poll: node=7
add: 7 → currentLevel=[15, 7]
无子节点
queue = []
result.add([15, 7])
result = [[3], [9, 20], [15, 7]]
队列为空,循环结束
返回: [[3], [9, 20], [15, 7]]
```
## 图解BFS过程
```
二叉树:
3
/ \
9 20
/ \
15 7
层序遍历过程:
第1层: queue=[3]
处理3,加入9和20
结果: [3]
第2层: queue=[9,20]
处理9(无子),处理20(加入15,7)
结果: [9,20]
第3层: queue=[15,7]
处理15(无子),处理7(无子)
结果: [15,7]
最终: [[3], [9,20], [15,7]]
```
## 关键:为什么需要 levelSize?
```
如果不记录levelSize:
queue = [3]
poll 3, add 9,20
queue = [9, 20]
poll 9, queue = [20]
poll 20, add 15,7
queue = [15, 7]
...
问题:不知道哪些节点属于同一层!
使用levelSize:
queue = [3]
levelSize = 1 ← 第1层有1个节点
处理1次,得到 [3]
queue = [9, 20]
levelSize = 2 ← 第2层有2个节点
处理2次,得到 [9, 20]
每层的节点数 = 处理前的队列大小
解法2:DFS(递归)
思路
用递归遍历,传递当前层数,将节点加入对应层的列表。
代码
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
dfs(root, 0, result);
return result;
}
private void dfs(TreeNode node, int level, List<List<Integer>> result) {
if (node == null) return;
// 如果当前层还没有列表,创建一个
if (level == result.size()) {
result.add(new ArrayList<>());
}
// 将当前节点加入对应层
result.get(level).add(node.val);
// 递归处理左右子树(下一层)
dfs(node.left, level + 1, result);
dfs(node.right, level + 1, result);
}
}
```
### DFS演示
```
二叉树:
3
/ \
9 20
/ \
15 7
递归调用树(前序遍历):
dfs(3, 0)
result[0] = [3]
├─ dfs(9, 1)
│ result[1] = [9]
│
│ ├─ dfs(null, 2) 返回
│ └─ dfs(null, 2) 返回
│
└─ dfs(20, 1)
result[1] = [9, 20]
├─ dfs(15, 2)
│ result[2] = [15]
│
│ ├─ dfs(null, 3) 返回
│ └─ dfs(null, 3) 返回
│
└─ dfs(7, 2)
result[2] = [15, 7]
├─ dfs(null, 3) 返回
└─ dfs(null, 3) 返回
最终 result = [[3], [9,20], [15,7]]
两种解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| BFS | O(n) | O(w) | 直观,符合题意 | 需要队列 |
| DFS | O(n) | O(h) | 代码简洁 | 不够直观 |
w = 树最大宽度,h = 树高度
变体1:自底向上层序遍历
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
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);
}
result.add(0, currentLevel); // 插入到开头
}
return result;
}
}
输出: [[15,7], [9,20], [3]]
变体2:锯齿形层序遍历
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean leftToRight = true; // 方向标志
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
if (leftToRight) {
currentLevel.add(node.val); // 正常添加
} else {
currentLevel.add(0, node.val); // 头部添加(反向)
}
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(currentLevel);
leftToRight = !leftToRight; // 切换方向
}
return result;
}
}
输出: [[3], [20,9], [15,7]]
边界情况
// 1. 空树
root = null
返回 []
// 2. 单节点
root = [1]
返回 [[1]]
// 3. 只有左子树
1
/
2
/
3
返回 [[1], [2], [3]]
// 4. 只有右子树
1
\
2
\
3
返回 [[1], [2], [3]]
// 5. 完全二叉树
1
/ \
2 3
/ \ / \
4 5 6 7
返回 [[1], [2,3], [4,5,6,7]]
测试用例
// 测试1
root = [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
输出: [[3],[9,20],[15,7]]
// 测试2
root = [1]
输出: [[1]]
// 测试3
root = []
输出: []
// 测试4
root = [1,2,3,4,null,null,5]
1
/ \
2 3
/ \
4 5
输出: [[1],[2,3],[4,5]]
常见错误
错误1:没有记录levelSize
// ✗ 错误(无法区分层)
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
// 所有节点混在一起
}
// ✓ 正确
while (!queue.isEmpty()) {
int levelSize = queue.size(); // 记录当前层节点数
for (int i = 0; i < levelSize; i++) {
// 处理当前层
}
}
错误2:在循环中使用queue.size()
// ✗ 错误(size会变化)
for (int i = 0; i < queue.size(); i++) {
TreeNode node = queue.poll();
queue.offer(node.left); // size增加了!
}
// ✓ 正确
int levelSize = queue.size(); // 先保存
for (int i = 0; i < levelSize; i++) {
// ...
}
错误3:DFS时忘记创建新层
// ✗ 错误(越界)
result.get(level).add(node.val); // level可能不存在
// ✓ 正确
if (level == result.size()) {
result.add(new ArrayList<>());
}
result.get(level).add(node.val);
复杂度分析
BFS
- 时间: O(n) --- 每个节点访问一次
- 空间 : O(w) --- 队列最大宽度
- 完全二叉树最后一层: O(n/2)
DFS
- 时间: O(n) --- 每个节点访问一次
- 空间 : O(h) --- 递归栈深度
- 平衡树: O(log n)
- 链状树: O(n)
本质
层序遍历的核心:
BFS思想:
- 用队列存储待访问节点
- 每次处理完一层再处理下一层
- levelSize 是关键,标识每层边界
与DFS对比:
- DFS(前中后序):深度优先,用栈/递归
- BFS(层序):广度优先,用队列
应用场景:
- 最短路径问题
- 逐层处理的问题
- 需要按层统计的问题