LeetCode 102. 二叉树的层序遍历:图文拆解+代码详解

0 阅读7分钟

LeetCode经典二叉树题目——102. 二叉树的层序遍历,这道题是二叉树广度优先搜索(BFS)的入门必刷题,也是面试中高频出现的基础题,不管是新手还是复盘,都值得好好吃透。

话不多说,先看题目本身,帮大家理清题意、拆解思路,再逐行解析代码,最后总结易错点,确保看完就能上手写对。

一、题目解读:什么是层序遍历?

题目给出二叉树的根节点root,要求返回其节点值的层序遍历,核心要求是「逐层地,从左到右访问所有节点」。

举个简单例子帮助理解:

如果二叉树是:

    3
   / \
  9  20
    /  \
   15   7

那么层序遍历的结果就是 [[3], [9,20], [15,7]] —— 第一层只有根节点3,第二层从左到右是9和20,第三层是15和7,每一层的节点值单独作为一个数组,最终组成二维数组返回。

这里要注意和「前中后序遍历」区分开:前中后序是深度优先(DFS),沿着一条路径走到底再回溯;而层序遍历是广度优先(BFS),先遍历完当前层的所有节点,再进入下一层,像“剥洋葱”一样逐层推进。

二、核心思路:用队列实现BFS

层序遍历的核心难点是「区分每一层的节点」—— 如何知道当前遍历的是哪一层,什么时候结束当前层、进入下一层?

解决这个问题的关键工具是「队列」(先进先出,FIFO),队列的特性刚好契合层序遍历“先遍历当前层所有节点,再处理下一层”的逻辑,具体思路分4步:

  1. 边界处理:如果根节点root为null,直接返回空数组(空树没有节点可遍历);

  2. 初始化:创建一个队列,将根节点入队(队列存储当前待处理的节点);创建一个结果数组,用于存储每一层的节点值;

  3. 循环处理(直到队列为空):

    • 记录当前队列的长度(也就是当前层的节点个数,记为levelSize),这个长度是区分层的关键;

    • 创建一个临时数组level,用于存储当前层的所有节点值;

    • 循环levelSize次,每次从队列头部取出一个节点:

      • 将该节点的值加入临时数组level;

      • 如果该节点有左孩子,将左孩子入队(为下一层遍历做准备);

      • 如果该节点有右孩子,将右孩子入队(同样为下一层遍历做准备);

    • 当前层遍历完成,将临时数组level加入结果数组;

  4. 循环结束,返回结果数组。

这里的核心技巧是「记录当前层的节点个数」—— 因为队列中会不断加入下一层的节点,只有通过levelSize限制循环次数,才能确保每次循环只处理当前层的节点,不会混入下一层的节点,从而正确区分每一层。

三、完整代码+逐行解析

先给出完整可运行的TypeScript代码(题目已提供TreeNode类,直接复用即可),再逐行拆解关键逻辑,新手也能看懂每一步的作用。

完整代码

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

class TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
  constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
    this.val = (val === undefined ? 0 : val)
    this.left = (left === undefined ? null : left)
    this.right = (right === undefined ? null : right)
  }
}

function levelOrder(root: TreeNode | null): number[][] {
  // 边界处理:空树返回空数组
  if (!root) {
    return []
  }
  // 队列:存储当前待处理的节点,初始入队根节点
  const queue: TreeNode[] = [root]
  // 结果数组:存储每一层的节点值
  const result: number[][] = []
  // 队列不为空,说明还有节点未处理
  while (queue.length) {
    // 记录当前层的节点个数(关键:区分每一层)
    const levelSize = queue.length
    // 临时数组:存储当前层的节点值
    const level: number[] = []
    // 循环处理当前层的所有节点(循环次数 =  当前层节点数)
    for (let i = 0; i < levelSize; i++) {
      // 从队列头部取出节点(先进先出)
      const node = queue.shift()
      // 防止node为null(理论上不会出现,严谨性处理)
      if (!node) continue;
      // 将当前节点值加入当前层数组
      level.push(node.val)
      // 左孩子存在,入队(下一层节点)
      if (node.left) {
        queue.push(node.left)
      }
      // 右孩子存在,入队(下一层节点)
      if (node.right) {
        queue.push(node.right)
      }
    }
    // 当前层处理完毕,加入结果数组
    result.push(level)
  }
  // 返回最终结果
  return result;
};

逐行关键解析

  1. 边界处理 if (!root) return []: 如果根节点为null,说明是一棵空树,没有任何节点,直接返回空数组,避免后续队列操作报错。

  2. 队列初始化 const queue: TreeNode[] = [root]: 队列是核心数据结构,初始时只有根节点,因为层序遍历从根节点开始。

  3. 循环条件 while (queue.length): 只要队列不为空,就说明还有节点没处理(可能是当前层剩余节点,也可能是下一层节点),循环继续。

  4. 记录当前层节点数 const levelSize = queue.length: 这是最关键的一步!假设当前队列中有3个节点,说明当前层有3个节点,后续循环3次,刚好把这3个节点处理完,不会混入下一层的节点。

  5. 临时数组 const level: number[] = []: 每一层都需要一个临时数组存储节点值,遍历完当前层后,将这个数组加入结果数组,保证结果的二维结构。

  6. 取出节点 const node = queue.shift(): shift()方法会删除并返回队列的第一个元素,契合队列“先进先出”的特性,确保先处理当前层的第一个节点(从左到右)。

  7. 左、右孩子入队: 处理完当前节点后,将其左、右孩子(如果存在)入队,这些孩子是下一层的节点,会在后续循环中被处理,从而实现“逐层遍历”。

  8. 加入结果数组 result.push(level): 当前层的所有节点都处理完毕后,将临时数组level加入result,完成一层的遍历。

四、易错点提醒(避坑必看)

这道题看似简单,但新手很容易踩坑,总结3个最常见的错误,帮大家避开:

  1. 忘记记录levelSize,直接循环queue.length: 错误原因:队列在循环中会不断加入下一层的节点,queue.length会动态变化,导致循环次数不对,无法区分层。正确做法:必须在每次while循环开始时,记录当前queue的长度(即levelSize),循环次数固定为levelSize。

  2. 忽略node为null的情况: 错误原因:虽然题目中root是TreeNode|null,但queue中理论上不会有null,但shift()方法在队列空时会返回undefined,加上if (!node) continue是严谨性处理,避免报错。

  3. 左、右孩子入队顺序错误: 错误原因:题目要求“从左到右”访问,若先入队右孩子、再入队左孩子,会导致当前层节点顺序颠倒。正确做法:先入队左孩子,再入队右孩子,确保左在前、右在后。

五、题目延伸与思考

这道题是层序遍历的基础版本,掌握后可以尝试它的变形题,巩固BFS思路:

  • LeetCode 107. 二叉树的层序遍历II:要求自底向上层序遍历(从最下层到最上层),只需将result数组反转即可;

  • LeetCode 199. 二叉树的右视图:层序遍历中,每次只保留当前层的最后一个节点值;

  • LeetCode 637. 二叉树的层平均值:层序遍历中,计算每一层的节点值平均值。

其实这些变形题的核心思路和本题一致,都是用队列实现BFS,只是在处理“每一层结果”时做了不同的操作,掌握了基础,变形题就能迎刃而解。

六、总结

LeetCode 102题的核心是「用队列实现BFS,通过记录当前层节点数区分每一层」,整体难度中等,是二叉树BFS的入门题。

关键记住3点:队列存节点、levelSize分层次、左孩子先入队,再结合边界处理和严谨性判断,就能轻松AC。

如果是新手,建议自己手动模拟一遍队列的操作过程(比如用上面举的例子,一步步推进队列、处理节点),能更直观地理解层序遍历的逻辑。