102. 二叉树的层序遍历 (Medium)

专题归类: 05-二叉树 LeetCode 链接: https://leetcode.cn/problems/binary-tree-level-order-traversal/


在线做题: 写 Python 代码并运行 · Java 跳转 LeetCode

题目描述

给你二叉树的根节点 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

题目详细分析

  • 数据范围含义: 节点数最多 2000,值范围 [-1000, 1000]。BFS 的队列最多存储一层的节点,最坏情况下满二叉树最后一层约 1000 个节点,内存完全 OK。
  • 输入输出特征: 输入根节点,输出二维列表——每个内层列表代表一层的节点值。
  • 边界条件: 空树返回 [],单节点返回 [[val]]
  • 核心约束: 分层输出,必须把同一层的节点放在同一个列表中,且保持从左到右的顺序。
  • 隐藏条件: 分层是核心要求。如果只是遍历而不分层,用简单队列即可;分层需要额外技巧(记录每层节点数或用分隔符)。

小白版直白理解

就像在操场上给学生们排队点名——先叫第一排的所有人(从左到右),记下他们的名字,然后让他们报出自己后面一排的人,再叫第二排,以此类推。层序遍历就是:从根节点(第一排)开始,逐排处理,每排从左到右。


解题思路

思路一:BFS 标准模板(推荐)

核心思想: 使用队列,每次处理一整层的节点。关键技巧是 for _ in range(len(q)):在处理每层开始时,队列中的节点数就是该层的节点数。通过这个长度控制循环次数,保证不会混到下一层。

为什么 BFS 适合层序? BFS 天然是按「层」为单位推进的——先访问所有距离为 1 的节点,再访问所有距离为 2 的节点。队列 FIFO 的特性保证了先入队的上层节点先被处理。

from collections import deque
 
def levelOrder(root):
    if not root:
        return []
 
    res = []
    q = deque([root])
 
    while q:
        level = []                    # 当前层的节点值列表
        level_size = len(q)           # 当前层的节点数(关键!)
        for _ in range(level_size):
            node = q.popleft()
            level.append(node.val)     # 访问当前节点
            if node.left:
                q.append(node.left)    # 左孩子入队
            if node.right:
                q.append(node.right)   # 右孩子入队
        res.append(level)              # 将当前层加入结果
 
    return res

思路二:DFS 递归(前序 + depth 参数)

核心思想: 用深度 depth 参数记录当前节点所在的层数。递归时始终保持 res[depth] 存在对应的列表。前序遍历(根→左→右)天然保证每层从左到右的顺序。

为什么 DFS 也能做层序? DFS 虽然是一条路走到黑,但通过 depth 参数可以知道每个节点属于哪一层,将它们放入对应的「层桶」中。但需要注意:必须先保证 res 的长度足够容纳当前层。

def levelOrder(root):
    res = []
 
    def dfs(node, depth):
        if not node:
            return
        # 如果当前层还没有列表,新建一个
        if depth == len(res):
            res.append([])
        res[depth].append(node.val)   # 放入对应层的列表中
        dfs(node.left, depth + 1)     # 递归左子树
        dfs(node.right, depth + 1)    # 递归右子树
 
    dfs(root, 0)
    return res

易错点

  • BFS 中 len(q) 必须在 for 循环前固定: 如果在 for 循环中动态取 len(q),由于循环中会 popleft 和 append,长度会不断变化。必须在一开始就把长度存到变量中。
  • DFS 中 res 的初始化顺序: 当第一次进入第 depth 层时,必须先 res.append([]),然后再赋值。如果 res 的长度 <= depth,说明这一层还没创建。
  • 空树返回 [] 而不是 [[]] 空树没有节点,所以返回空列表而不是包含一个空列表。
  • BFS 不要用列表模拟队列(pop(0) 是 O(n)): 一定要用 collections.deque,它的 popleft() 是 O(1)。

框架提炼

BFS 层序遍历模板(必记):

def levelOrder(root):
    if not root:
        return []
    res = []
    q = deque([root])
    while q:
        level = []
        for _ in range(len(q)):   # 固定当前层长度
            node = q.popleft()
            level.append(node.val)
            if node.left: q.append(node.left)
            if node.right: q.append(node.right)
        res.append(level)
    return res

变体思路:for _ in range(len(q)) 改为 for i in range(len(q)) 可以在循环中知道当前节点在层中的位置(如右视图问题中判断 i == len(q)-1)。


关联题目