【力扣100题】31.二叉树的层序遍历

一、题目描述

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

示例

示例 输入 输出
示例1 root = [3,9,20,null,null,15,7] [[3],[9,20],[15,7]]
示例2 root = [1] [[1]]
示例3 root = [] []
复制代码
示例1的树结构:
        3
       / \
      9   20
          / \
         15  7

层序遍历结果:
第1层:[3]
第2层:[9, 20]
第3层:[15, 7]

输出:[[3], [9,20], [15,7]]

提示

  • 树中节点数目在范围 [0, 2000]
  • -1000 <= Node.val <= 1000

二、解题思路总览

方法 核心思想 时间复杂度 空间复杂度
迭代(BFS) 队列 + 分层记录 O(n) O(n)

核心思想

  • 使用 队列(FIFO)实现 BFS
  • 关键技巧:先记录当前层节点数 n = q.size(),再处理这一层
  • 每层处理完后再处理下一层

BFS vs DFS

  • BFS(层序遍历):用队列,按层次一层层往下
  • DFS(深度遍历):用栈/递归,先深后浅

三、完整代码

cpp 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> ans;      // 1. 存放最终结果
        queue<TreeNode*> q;          // 2. BFS遍历用队列

        if (root != NULL) q.push(root); // 3. 根节点入队

        while (!q.empty()) {         // 4. 队列不为空时循环
            int n = q.size();        // 5. 当前层的节点数(关键!)
            vector<int> tmp;          // 6. 存放当前层的节点值

            // 7. 处理当前层的所有节点
            for (int i = 0; i < n; i++) {
                TreeNode* node = q.front();  // 取队首节点
                q.pop();                     // 出队

                tmp.push_back(node->val);    // 保存节点值

                // 8. 将下一层子节点入队
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }

            ans.push_back(tmp);       // 9. 将当前层结果加入答案
        }

        return ans;                   // 10. 返回层序遍历结果
    }
};

四、算法流程图(ASCII)

树结构与遍历过程

复制代码
        3           ← 第1层
       / \
      9   20       ← 第2层
          / \
         15  7      ← 第3层

队列变化过程:

初始:q = [3]

第1轮循环(n=1,处理第1层):
  出队3,tmp=[3],入队9和20
  q = [9, 20]
  ans = [[3]]

第2轮循环(n=2,处理第2层):
  出队9,tmp=[9]
  出队20,tmp=[9,20],入队15和7
  q = [15, 7]
  ans = [[3], [9,20]]

第3轮循环(n=2,处理第3层):
  出队15,tmp=[15]
  出队7,tmp=[15,7]
  q = []
  ans = [[3], [9,20], [15,7]]

队列为空,结束

核心流程图

复制代码
┌─────────────────────────────────────────────────────────┐
│                    层序遍历主循环                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   while (!q.empty()) {                                  │
│       int n = q.size();  ← 先记录当前层节点数            │
│       vector<int> tmp;                                  │
│                                                         │
│       for (int i = 0; i < n; i++) {  ← 只处理这一层     │
│           TreeNode* node = q.front();                   │
│           q.pop();                                      │
│           tmp.push_back(node->val);                     │
│           if (node->left) q.push(node->left);          │
│           if (node->right) q.push(node->right);        │
│       }                                                 │
│                                                         │
│       ans.push_back(tmp);  ← 当前层处理完再下一层       │
│   }                                                     │
│                                                         │
└─────────────────────────────────────────────────────────┘

关键点:
1. 先记录 q.size(),因为入队操作会改变队列大小
2. 只用 for 循环处理当前层,for 结束后才是下一层
3. 每层处理完才 push_back 到 ans

五、逐行解析

cpp 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        // ─────────────────────────────────────────
        // 第1步:创建结果容器
        // 外层vector存储每一层,内层vector存储每层的节点值
        // ─────────────────────────────────────────
        vector<vector<int>> ans;

        // ─────────────────────────────────────────
        // 第2步:创建队列
        // 队列用于BFS,保证先入队的节点先被处理(按层次顺序)
        // ─────────────────────────────────────────
        queue<TreeNode*> q;

        // ─────────────────────────────────────────
        // 第3步:初始化
        // 如果根节点不为空,将其入队
        // ─────────────────────────────────────────
        if (root != NULL) q.push(root);

        // ─────────────────────────────────────────
        // 第4步:主循环
        // 队列为空时说明所有节点都处理完了
        // ─────────────────────────────────────────
        while (!q.empty()) {
            // ─────────────────────────────────────────
            // 第5步:记录当前层节点数(关键技巧!)
            //
            // 必须在 for 循环之前记录
            // 因为循环内会入队新节点,改变 q.size()
            // ─────────────────────────────────────────
            int n = q.size();

            // ─────────────────────────────────────────
            // 第6步:创建当前层的临时容器
            // ─────────────────────────────────────────
            vector<int> tmp;

            // ─────────────────────────────────────────
            // 第7步:处理当前层的所有节点
            //
            // 注意:只处理 n 个节点(当前层的节点数)
            // 处理过程中会把下一层节点入队,但 for 不会处理它们
            // ─────────────────────────────────────────
            for (int i = 0; i < n; i++) {
                TreeNode* node = q.front();  // 取出队首节点
                q.pop();                     // 队首出队

                tmp.push_back(node->val);    // 保存节点值

                // ─────────────────────────────────────────
                // 第8步:将下一层子节点入队
                // 左子节点先入队,保证左子节点先被处理
                // ─────────────────────────────────────────
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }

            // ─────────────────────────────────────────
            // 第9步:将当前层结果加入答案
            // 此时 tmp 中存储了这一层所有节点的值
            // ─────────────────────────────────────────
            ans.push_back(tmp);
        }

        // ─────────────────────────────────────────
        // 第10步:返回结果
        // ─────────────────────────────────────────
        return ans;
    }
};

六、复杂度分析

时间复杂度

分析 复杂度
每个节点入队一次、出队一次 O(n)

推导

  • n 个节点
  • 每个节点最多入队一次、出队一次
  • 每次入队出队是 O(1) 操作

空间复杂度

分析 复杂度
队列最多存储一层的节点 O(n)

推导

  • 最宽的一层最多有 n/2 个节点(完全二叉树最后一层)
  • 但队列实际存储的是当前层 + 下一层的节点
  • 精确分析:O(n)

七、面试追问 FAQ

问题 回答
为什么先记录 n = q.size() 因为 for 循环内会入队新节点改变队列大小
为什么不直接在 while 循环内用 q.size() 会导致死循环,因为每出队一个就入队两个
BFS 和 DFS 的区别? BFS 按层次遍历,用队列;DFS 先深后浅,用栈或递归
如何区分层数? 每轮 for 循环对应一层,循环次数就是层数
如何实现之字形层序遍历(第103题)? 用双端队列,或加标志位判断奇偶层反转

八、相关题目

题目 难度 关键点
102. 二叉树的层序遍历 中等 本题
107. 二叉树的层序遍历 II 中等 从底层往上
103. 二叉树的锯齿形层序遍历 中等 之字形
104. 二叉树的最大深度 简单 层序遍历统计层数
111. 二叉树的最小深度 简单 层序遍历找第一个叶子

九、总结

对比项 说明
代码行数 核心15行
时间复杂度 O(n)
空间复杂度 O(n)
核心数据结构 队列
核心技巧 先记录 q.size() 再处理当前层

核心模板

cpp 复制代码
while (!q.empty()) {
    int n = q.size();        // 先记录当前层节点数
    for (int i = 0; i < n; i++) {
        TreeNode* node = q.front();
        q.pop();
        // 处理 node...
        if (node->left) q.push(node->left);
        if (node->right) q.push(node->right);
    }
    ans.push_back(tmp);
}

相关推荐
To_OC4 小时前
LC 131 分割回文串:刚学回溯时,我连怎么切字符串都想不明白
javascript·算法·leetcode
旖-旎5 小时前
LeetCode 518:零钱兑换||(完全背包)—— 题解
c++·算法·leetcode·动态规划·背包问题
To_OC6 小时前
LC 42 接雨水:暴力超时卡半天?前后缀数组一用就通了
javascript·算法·leetcode
delishcomcn7 小时前
AI视觉识别+分切算法:电化铝缺陷检测与裁切一体化解锁
人工智能·算法
触底反弹7 小时前
深入理解大模型采样:Temperature、Top-K、Top-P 的原理与实战
人工智能·算法·面试
雪碧聊技术7 小时前
力扣 LCR 091. 粉刷房子 —— 动态规划入门详解
算法·动态规划
CV-Climber10 小时前
检索技术的实际应用
人工智能·算法
hhzz11 小时前
Tiger AI Platform平台中增加人脸识别功能
图像处理·人工智能·算法·计算机视觉·大模型
从零开始的代码生活_11 小时前
C++ 继承详解:访问控制、对象模型、菱形继承与设计取舍
开发语言·c++·后端·学习·算法