【力扣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);
}

相关推荐
8Qi87 小时前
LeetCode 75:颜色分类(荷兰国旗问题)—— Java 题解 ✅
java·算法·leetcode·指针·排序
888CC++9 小时前
如何在 C 语言中进行程序调试?
前端·javascript·算法
(●—●)橘子……10 小时前
力扣第503场周赛练习理解
python·学习·算法·leetcode·职场和发展·周赛
明志数科12 小时前
4D时序标注技术详解:让机器人理解连续动作的数据基础
java·算法·机器人
KaMeidebaby12 小时前
卡梅德生物技术快报|原核表达系统工艺优化:包涵体重折叠 + 分子筛纯化实现功能 RBD 高效制备,附全参数配置
前端·人工智能·算法·数据挖掘·数据分析
无限码力12 小时前
携程0510笔试真题【单数组交换】
算法·携程笔试·携程笔试真题·携程0510笔试真题
BlockWay13 小时前
WEEX Labs 周度观察:微软-OpenAI 合作调整与AI 多云趋势
大数据·人工智能·算法·安全·microsoft
风筝在晴天搁浅13 小时前
快手 CodeTop LeetCode 224.基本计算器
数据结构·算法·leetcode
Smoothcloud润云13 小时前
5大功能精修,重构AI算力使用体验!
java·人工智能·windows·算法·重构·编辑器·sublime text