力扣解题-102. 二叉树的层序遍历

12 阅读6分钟

力扣解题-102. 二叉树的层序遍历

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

示例 1:

image.png

输入: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),是层序遍历的经典且最优解法。

核心逻辑拆解

层序遍历的核心是“按层访问、从左到右”,关键在于隔离每一层的节点避免跨层处理:

  1. 空树处理:若根节点root == null,直接返回空列表(无任何节点可遍历);
  2. 初始化:创建结果列表res(存储各层节点值列表),队列queue(存储待遍历节点),先将根节点入队;
  3. 层级遍历循环:队列非空时,持续处理每一层:
    • 创建层列表:为当前层新建空列表list,用于存储该层所有节点值;
    • 固定层大小:遍历前记录队列长度queue.size()(即当前层的节点数),通过倒序循环(i>0)确保只处理当前层节点(避免遍历中入队的下一层节点干扰);
    • 处理当前层节点:逐个弹出队列节点,将节点值加入层列表,同时将节点的左右子节点入队(为下一层遍历做准备);
    • 保存层结果:当前层所有节点处理完毕后,将层列表加入结果列表;
  4. 返回结果:遍历完所有层后,返回存储各层节点值的结果列表。
具体步骤(以示例1 root=[3,9,20,null,null,15,7]为例)
遍历层级队列初始状态层大小层列表收集过程队列最终状态结果列表更新
0(根层)[3]1list = [3][9,20][[3]]
1[9,20]2list = [9,20][15,7][[3],[9,20]]
2[15,7]2list = [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个节点);
  • 优势:
    1. 逻辑直观,完全贴合“层序遍历”的语义,易理解和维护;
    2. 一次遍历完成,无冗余操作,效率最优;
    3. 代码结构清晰,符合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);
}
核心逻辑说明
  1. 递归参数level表示当前节点的层级(根节点为0),res为存储层序结果的列表;
  2. 层级初始化:当level == res.size()时,说明是首次访问该层级,需为该层级新建空列表;
  3. 节点值存储:将当前节点值加入对应层级的列表,保证层内节点“从左到右”的顺序;
  4. 递归遍历:按“左→右”顺序递归遍历子树,确保层内节点顺序符合题目要求。
性能说明
  • 时间复杂度:O(n)(每个节点仅被访问一次);
  • 空间复杂度:O(h)(递归栈深度等于树的高度,平衡树为O(logn),斜树为O(n));
  • 优势:
    1. 无需使用队列,空间复杂度在平衡树场景下优于BFS;
    2. 代码简洁,无需处理队列的入队/出队操作;
  • 劣势:
    1. 逻辑稍抽象,依赖递归栈,极端斜树场景可能导致栈溢出;
    2. 本质是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),简化代码;
  • 可读性提升:变量命名更语义化(如levelSizelevelList),代码逻辑更清晰。

总结

  1. BFS迭代法(第一次解答):O(n)时间+O(n)空间,逻辑直观、效率最优,是层序遍历的工程首选解法;
  2. DFS递归法:O(n)时间+O(h)空间,无需队列,适合理解层级遍历的另一种思路,平衡树场景下空间更优;
  3. BFS优化版:O(n)时间+O(n)空间,循环写法更规范,可读性更高;
  4. 关键技巧
    • 核心思想:层序遍历的关键是“区分层级”,BFS通过队列大小固定层级,DFS通过层级参数标记层级;
    • 顺序保证:无论BFS还是DFS,都需保证“先左后右”遍历子节点,确保层内节点顺序正确;
    • 边界处理:必须单独处理空树场景,返回空列表符合题目要求。