力扣解题-102. 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。(即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
树中节点数目在范围 [0, 2000] 内
-1000 <= Node.val <= 1000
Related Topics
树、广度优先搜索、二叉树
第一次解答
解题思路
核心方法:广度优先搜索(BFS)层级遍历法,利用队列实现二叉树的层级遍历,通过固定每一层的节点数量来精准区分不同层级,逐层收集节点值,时间复杂度O(n)、空间复杂度O(n),是层序遍历的经典且最优解法。
核心逻辑拆解
层序遍历的核心是“按层访问、从左到右”,关键在于隔离每一层的节点避免跨层处理:
- 空树处理:若根节点
root == null,直接返回空列表(无任何节点可遍历); - 初始化:创建结果列表
res(存储各层节点值列表),队列queue(存储待遍历节点),先将根节点入队; - 层级遍历循环:队列非空时,持续处理每一层:
- 创建层列表:为当前层新建空列表
list,用于存储该层所有节点值; - 固定层大小:遍历前记录队列长度
queue.size()(即当前层的节点数),通过倒序循环(i>0)确保只处理当前层节点(避免遍历中入队的下一层节点干扰); - 处理当前层节点:逐个弹出队列节点,将节点值加入层列表,同时将节点的左右子节点入队(为下一层遍历做准备);
- 保存层结果:当前层所有节点处理完毕后,将层列表加入结果列表;
- 创建层列表:为当前层新建空列表
- 返回结果:遍历完所有层后,返回存储各层节点值的结果列表。
具体步骤(以示例1 root=[3,9,20,null,null,15,7]为例)
| 遍历层级 | 队列初始状态 | 层大小 | 层列表收集过程 | 队列最终状态 | 结果列表更新 |
|---|---|---|---|---|---|
| 0(根层) | [3] | 1 | list = [3] | [9,20] | [[3]] |
| 1 | [9,20] | 2 | list = [9,20] | [15,7] | [[3],[9,20]] |
| 2 | [15,7] | 2 | list = [15,7] | [] | [[3],[9,20],[15,7]] |
关键细节说明
- 层大小固定:必须在遍历当前层前获取
queue.size(),因为遍历过程中会将下一层节点入队,队列长度会动态变化,若遍历中获取会导致处理跨层节点; - 倒序循环:
for(int i=queue.size();i>0;i--)比正序循环更直观,无需额外变量记录初始长度,避免循环次数错误; - 冗余判断说明:代码中
if(value!=null)是鲁棒性设计(题目中节点值非null),不影响核心逻辑,但可兼容特殊异常场景; - 节点入队顺序:先左子节点后右子节点,保证层内节点“从左到右”的访问顺序,符合题目要求。
性能说明
- 时间复杂度:O(n)(每个节点仅入队/出队一次,n为节点总数);
- 空间复杂度:O(n)(最坏情况队列存储一层所有节点,如完全二叉树最后一层有n/2个节点);
- 优势:
- 逻辑直观,完全贴合“层序遍历”的语义,易理解和维护;
- 一次遍历完成,无冗余操作,效率最优;
- 代码结构清晰,符合BFS遍历的经典范式,可拓展性强(如求层平均值、层最大值等)。
public List<List<Integer>> levelOrder(TreeNode root) {
if(root==null){
return new ArrayList<>();
}
List<List<Integer>> res=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> list=new ArrayList<>();
for(int i=queue.size();i>0;i--){
TreeNode node = queue.poll();
Integer value=node.val;
if(value!=null){
list.add(value);
}
if(node.left!=null){
queue.offer(node.left);
}
if(node.right!=null){
queue.offer(node.right);
}
}
res.add(list);
}
return res;
}
示例解答
解题思路
解法1:DFS递归法(拓展思路)
核心方法:深度优先搜索(DFS)递归标记层级,通过递归遍历二叉树,用层级参数标记节点所属层级,将节点值加入对应层级的列表,最终实现层序遍历的效果,时间复杂度O(n)、空间复杂度O(h)(h为树的高度)。
代码实现
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
// 从根节点(层级0)开始递归
dfs(root, 0, res);
return res;
}
private void dfs(TreeNode node, int level, List<List<Integer>> res) {
if (node == null) {
return;
}
// 首次访问该层级,初始化空列表
if (level == res.size()) {
res.add(new ArrayList<>());
}
// 将节点值加入对应层级的列表
res.get(level).add(node.val);
// 递归遍历左右子树,层级+1(先左后右保证层内顺序)
dfs(node.left, level + 1, res);
dfs(node.right, level + 1, res);
}
核心逻辑说明
- 递归参数:
level表示当前节点的层级(根节点为0),res为存储层序结果的列表; - 层级初始化:当
level == res.size()时,说明是首次访问该层级,需为该层级新建空列表; - 节点值存储:将当前节点值加入对应层级的列表,保证层内节点“从左到右”的顺序;
- 递归遍历:按“左→右”顺序递归遍历子树,确保层内节点顺序符合题目要求。
性能说明
- 时间复杂度:O(n)(每个节点仅被访问一次);
- 空间复杂度:O(h)(递归栈深度等于树的高度,平衡树为O(logn),斜树为O(n));
- 优势:
- 无需使用队列,空间复杂度在平衡树场景下优于BFS;
- 代码简洁,无需处理队列的入队/出队操作;
- 劣势:
- 逻辑稍抽象,依赖递归栈,极端斜树场景可能导致栈溢出;
- 本质是DFS,需通过层级参数“映射”到层序结果,不如BFS直观。
解法2:BFS优化版(正序循环)
核心方法:在原BFS基础上优化循环写法,采用正序循环并提前记录层大小,逻辑与原解法一致但更符合常规编码习惯。
代码实现
public List<List<Integer>> levelOrder(TreeNode root) {
if (root == null) {
return new ArrayList<>();
}
List<List<Integer>> res = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
// 提前记录当前层的节点数
int levelSize = queue.size();
List<Integer> levelList = new ArrayList<>();
// 正序循环处理当前层节点
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
levelList.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
res.add(levelList);
}
return res;
}
优化说明
- 循环写法优化:将
for(int i=queue.size();i>0;i--)改为for(int i=0;i<levelSize;i++),提前记录层大小levelSize,更符合大多数开发者的编码习惯; - 冗余判断移除:去掉
if(value!=null)判断(题目保证节点值非null),简化代码; - 可读性提升:变量命名更语义化(如
levelSize、levelList),代码逻辑更清晰。
总结
- BFS迭代法(第一次解答):O(n)时间+O(n)空间,逻辑直观、效率最优,是层序遍历的工程首选解法;
- DFS递归法:O(n)时间+O(h)空间,无需队列,适合理解层级遍历的另一种思路,平衡树场景下空间更优;
- BFS优化版:O(n)时间+O(n)空间,循环写法更规范,可读性更高;
- 关键技巧:
- 核心思想:层序遍历的关键是“区分层级”,BFS通过队列大小固定层级,DFS通过层级参数标记层级;
- 顺序保证:无论BFS还是DFS,都需保证“先左后右”遍历子节点,确保层内节点顺序正确;
- 边界处理:必须单独处理空树场景,返回空列表符合题目要求。