实现二叉树的层序遍历。

考查重点:队列遍历与层级边界。用队列保存待访问节点,每轮固定当前队列长度,取出该层并入队子节点;空树返回空结果

本题目录

参考答案

核心回答

层序遍历用队列做广度优先搜索,按从上到下、每层从左到右的顺序访问节点。 根节点先入队,每次从队首取出节点,再依次把非空左孩子、右孩子加入队尾。

如果结果需要按层分组,每轮先记录当前队列长度,只处理这么多个节点;本轮加入的孩子留到下一轮。空树返回空列表。每个节点入队、出队各一次,时间复杂度为 O(n);辅助队列占 O(w),w 为最大层宽,保存全部结果另需 O(n)。

Python 实现

下面返回按层分组的节点值,使用 Python 3.10 及以上版本。

from __future__ import annotations

from collections import deque
from dataclasses import dataclass


@dataclass
class TreeNode:
    val: int
    left: TreeNode | None = None
    right: TreeNode | None = None


def level_order(root: TreeNode | None) -> list[list[int]]:
    if root is None:
        return []

    queue = deque([root])
    levels = []
    while queue:
        level_size = len(queue)
        values = []
        for _ in range(level_size):
            node = queue.popleft()
            values.append(node.val)
            if node.left is not None:
                queue.append(node.left)
            if node.right is not None:
                queue.append(node.right)
        levels.append(values)
    return levels


root = TreeNode(1, TreeNode(2, right=TreeNode(4)), TreeNode(3))
assert level_order(root) == [[1], [2, 3], [4]]
assert level_order(None) == []

为什么先记录本层长度

在例子中,队列按以下过程变化,队首写在左边:

  • 第一轮开始:[1],本层只有一个节点。处理 1 后,队列变成 [2, 3],得到第一层 [1]。
  • 第二轮开始:[2, 3],固定本层长度为 2。处理 2 后加入 4,队列是 [3, 4];接着只处理 3,将 4 留给下一轮,得到 [2, 3]。
  • 第三轮开始:[4],得到 [4],队列清空。

因此,外层循环开始时队列保存当前层;内层执行期间可能混有当前层剩余节点与下一层节点。如果内层一直处理到队列为空,刚加入的孩子也会被算进当前层,分层就丢失了。

左孩子先入队保证同一父节点的孩子从左到右访问;父节点本身按从左到右出队,又保证不同父节点的孩子保持正确顺序。

复杂度与边界

每个节点只处理一次,每条父子边只检查一次,所以总时间为 O(n)。Python 的 deque.append 和 popleft 适合队列两端操作;若用列表反复 pop(0),移动剩余元素可能使总开销在宽树上达到 O(n²)。

队列会短暂混合相邻两层,空间与最大层宽同阶,为 O(w),最坏 O(n)。单链树只有一个待处理节点,但返回结果仍包含全部 n 个值,因此不能把总空间也写成 O(1)。

实现要覆盖空树、单节点、只有一侧孩子、不满二叉树、重复值和零值。是否存在节点应检查节点引用,而不是用 node.val 的真假判断。输入按正常二叉树处理,孩子关系没有环且每个非根节点只有一个父节点。

常见追问

只需要一维访问序列呢? 出队时直接把值加入同一个结果列表,不必记录每层长度。若已经得到分层结果,也可以按层展开。

能用 DFS 返回相同分层结果吗? 可以在递归时携带深度,将值加入对应深度的列表,并先遍历左子树再遍历右子树。这样能得到分层输出,但实际访问过程仍是 DFS,递归栈占 O(h),h 为树高;层数很深时还需考虑递归深度。

遍历一般图还可以不判重吗? 树的孩子关系中不会重复到达同一节点;一般图可能有环或多条路径,通常要在节点入队时标记已发现,避免重复入队。

参考资料

  • 国内:Krahets / Hello 算法:二叉树遍历:层序遍历的队列过程和复杂度分析说明 FIFO 为什么能逐层推进;本文进一步加入空树处理与按层分组。
  • 国内:Doocs:二叉树的层序遍历:依据每轮固定队列长度的 BFS 实现,解释正文中新入队孩子为何必须留到下一层。
  • 国内:OI Wiki:BFS:借鉴一般图逐层扩展时的发现标记,说明正文中树遍历与一般图判重要求的区别。
  • 国外:Pat Morin:BinaryTree — A Basic Binary Tree:广度优先遍历部分从根节点入队、依次加入左右孩子,解释节点访问顺序与队列操作的关系。
  • 国外:Python 官方文档:deque:两端添加、弹出的性能与列表头部删除的代价,说明本实现选择 deque 的原因。

相关面经

11 篇

拼多多、荣耀、立元科技、得物、华橙

面经合集
查看原面经 →
手撕:最近公共祖先+BFS二叉树结合体,但是要完整实现二叉树、输入输出

字节跳动

AI Agent 开发工程师(质量方向)
一面
查看原面经 →
树的遍历方式有哪些

百度

前端开发工程师
秋招
查看原面经 →
二叉树前序 / 中序 / 后序 / 层序遍历分别是什么?
二叉树的层序遍历

百度

面经合集
查看原面经 →
手撕:二叉树前序遍历 + 层序遍历

知乎

全栈开发工程师
秋招一面
查看原面经 →
手撕二叉树层序遍历
展开剩余 6 篇面经

美团

后端开发工程师(AI)
一面
查看原面经 →
算法题:二叉树的层序遍历

百度

大模型研发工程师(Agent 方向)
秋招一面、二面
查看原面经 →
层序遍历。

哔哩哔哩(B站)

Agent 开发
社招二面
查看原面经 →
手撕:判断链表有环 & 树的层序遍历

知乎

后端开发
实习一面
查看原面经 →
算法考得很简单,二叉树的层序遍历,估计不想为难我,面试官很好

美团

后端开发
暑期实习一面
查看原面经 →
二叉树层序遍历

京东

AI 后端 / Agent 开发
日常实习一面
查看原面经 →
算法:二叉树的层序遍历

收录题集

2 个来源,共4 份题集

相关题目

4 题