2026/2/17 20:13:08
网站建设
项目流程
博兴建设局网站,绍兴市柯桥区建设局网站,网站建设中英文,湖南天辰建设责任公司网站Java版LeetCode热题100之二叉树的层序遍历#xff1a;从BFS到多维拓展的全面解析本文将深入剖析 LeetCode 第102题「二叉树的层序遍历」#xff0c;不仅提供广度优先搜索#xff08;BFS#xff09;的标准解法#xff0c;还涵盖算法原理、复杂度分析、面试技巧、工程应用及…Java版LeetCode热题100之二叉树的层序遍历从BFS到多维拓展的全面解析本文将深入剖析 LeetCode 第102题「二叉树的层序遍历」不仅提供广度优先搜索BFS的标准解法还涵盖算法原理、复杂度分析、面试技巧、工程应用及关联题目拓展。全文约9500字结构完整、内容翔实适合准备面试或夯实算法基础的开发者阅读。一、原题回顾题目编号LeetCode 102题目名称Binary Tree Level Order Traversal二叉树的层序遍历难度等级Medium基础但重要题目描述给你二叉树的根节点root返回其节点值的层序遍历。层序遍历逐层地从左到右访问所有节点。输出格式返回一个列表的列表其中每个子列表包含一层的节点值。示例示例 1输入root [3,9,20,null,null,15,7] 输出[[3],[9,20],[15,7]]树结构如下3 / \ 9 20 / \ 15 7示例 2输入root [1] 输出示例 3输入root [] 输出[]约束条件树中节点数目在范围[0, 2000]内-1000 Node.val 1000二、原题分析什么是“层序遍历”定义按树的层级顺序从上到下、从左到右访问节点。别名广度优先遍历BFS, Breadth-First Search输出要求必须按层分组不能简单返回扁平列表。为什么这道题重要BFS 的经典应用是理解队列和层级遍历的基石。面试必考题几乎所有大厂都会考察层序遍历及其变种。基础模板后续许多问题如锯齿形遍历、右视图都基于此。易错点如何正确分层是初学者的主要障碍。核心挑战普通 BFS 只能保证访问顺序但无法自动分层需要巧妙设计才能按层输出。三、答案构思面对“层序遍历”问题关键是如何区分不同层的节点。✅ 正确思路广度优先搜索BFS 层级控制核心思想使用队列存储待访问节点每次处理整层节点先记录当前队列大小即当前层节点数再循环处理实现方式外层while控制层数内层for处理当前层所有节点并将下一层节点入队❌ 错误思路普通 BFS不分层// 错误输出为 [3,9,20,15,7]未分层QueueTreeNodequeuenewLinkedList();queue.offer(root);while(!queue.isEmpty()){TreeNodenodequeue.poll();result.add(node.val);// 扁平列表// ...}我们将实现正确解法并深入分析其原理。四、完整答案Java实现方法广度优先搜索BFSimportjava.util.*;classSolution{publicListListIntegerlevelOrder(TreeNoderoot){ListListIntegerresultnewArrayList();// 边界条件空树if(rootnull){returnresult;}QueueTreeNodequeuenewLinkedList();queue.offer(root);while(!queue.isEmpty()){// 关键记录当前层的节点数量intlevelSizequeue.size();ListIntegercurrentLevelnewArrayList();// 处理当前层的所有节点for(inti0;ilevelSize;i){TreeNodenodequeue.poll();currentLevel.add(node.val);// 将下一层节点加入队列if(node.left!null){queue.offer(node.left);}if(node.right!null){queue.offer(node.right);}}// 将当前层结果加入最终结果result.add(currentLevel);}returnresult;}}✅使用 ArrayDeque 提升性能QueueTreeNodequeuenewArrayDeque();// 比 LinkedList 更高效五、代码分析核心逻辑详解层级控制int levelSize queue.size()是最关键的一步在进入内层循环前队列中恰好包含当前层的所有节点levelSize锁定了本次要处理的节点数避免受新入队节点影响执行流程以示例1为例初始: queue[3] 第1层: levelSize1 → poll(3) → add(9,20) → result 第2层: levelSize2 → poll(9,20) → add(15,7) → result[[3],[9,20]] 第3层: levelSize2 → poll(15,7) → 无新节点 → result[[3],[9,20],[15,7]]记忆技巧“先数人头再集体行动”为什么这个方法正确通过循环不变式证明初始化第1层只有根节点队列大小1正确保持若第k层处理正确则其子节点第k1层会按从左到右顺序入队终止队列空时所有层已处理完毕六、时间复杂度与空间复杂度分析指标复杂度说明时间复杂度O(n)每个节点入队出队各一次空间复杂度O(w)w 为树的最大宽度最坏 O(n)详细解释时间复杂度O(n)每个节点被访问恰好一次每次操作入队、出队、添加到列表均为常数时间故总时间为线性空间复杂度O(w)空间消耗主要来自队列存储队列最大长度 树的最大宽度w最坏情况完全二叉树最后一层有≈n/2节点 → O(n)最好情况链表每层1节点 → O(1)对比 DFS若用 DFS 实现层序遍历需额外存储层级信息空间复杂度仍为 O(n)且代码更复杂。七、常见问题解答FAQQ1为什么不能在内层循环中直接用queue.size()答因为queue.size()在循环中会动态变化例如// 错误写法for(inti0;iqueue.size();i){// size() 不断减小// ...}这会导致只处理部分节点实际处理size()/2个因为i增加的同时size()减少。Q2能否用 DFS 实现层序遍历答可以但不推荐。DFS 需要额外参数记录深度publicvoiddfs(TreeNodenode,intdepth,ListListIntegerresult){if(nodenull)return;if(depthresult.size()){result.add(newArrayList());}result.get(depth).add(node.val);dfs(node.left,depth1,result);dfs(node.right,depth1,result);}优点代码简洁缺点递归栈空间 O(h)且不符合“广度优先”的直观理解Q3如果树非常宽队列会内存溢出吗答理论上可能但题目约束n ≤ 2000完全二叉树最大宽度约1000内存足够。实际工程中可考虑分批处理。Q4如何实现从右到左的层序遍历答只需在添加子节点时先右后左if(node.right!null)queue.offer(node.right);if(node.left!null)queue.offer(node.left);但注意这会改变下一层的顺序若要保持每层从右到左但整体仍从上到下需用Collections.reverse(currentLevel)。Q5能否不用队列答可以用两个列表交替ListTreeNodecurrentArrays.asList(root);while(!current.isEmpty()){ListTreeNodenextnewArrayList();// 处理 current// 将子节点加入 nextcurrentnext;}但本质上仍是 BFS只是手动管理“层”。八、优化思路1. 使用 ArrayDeque 替代 LinkedListArrayDeque基于循环数组内存连续缓存友好LinkedList基于双向链表对象开销大QueueTreeNodequeuenewArrayDeque();2. 预分配列表容量若已知每层节点数可预分配ArrayList容量ListIntegercurrentLevelnewArrayList(levelSize);减少动态扩容开销。3. 提前终止针对变种问题如求“最小深度”遇到第一个叶子节点即可返回本题需完整遍历无法提前终止4. 并行处理理论上可并行处理同一层的节点但节点处理无依赖可并行但入队操作需同步可能得不偿失通常不值得5. 流式处理大数据场景若树极大可设计流式 API逐层返回结果而不全量存储StreamListIntegerlevelOrderStream(TreeNoderoot){...}九、数据结构与算法基础知识点回顾1. BFS vs DFS 对比特性BFSDFS数据结构队列FIFO栈LIFO递归或显式空间复杂度O(w)O(h)适用场景最短路径、层级遍历路径查找、回溯层序遍历天然支持需额外深度参数2. 队列的核心作用FIFO 保证层级顺序先入队的节点先处理层级隔离通过size()实现层间边界3. 树的宽度与高度宽度Width某层最多节点数高度Height根到最远叶子的边数完全二叉树性质高度 h 的树宽度最大为 2^h4. 循环不变式Loop Invariant定义循环前后始终为真的条件作用证明算法正确性本题不变式每次外层循环开始时队列包含当前层所有节点5. 边界条件处理空树直接返回空列表单节点正确处理为[[val]]null 子节点入队前检查避免 NullPointerException十、面试官提问环节模拟对话面试官你用了 BFS能说说为什么需要levelSize吗你因为 BFS 队列会动态变化。levelSize锁定了当前层的节点数确保内层循环只处理这一层不受新入队节点影响。面试官空间复杂度是多少你O(w)w 是树的最大宽度。完全二叉树中 w ≈ n/2所以最坏 O(n)。面试官如果要求从底向上返回层序遍历呢你可以在最后Collections.reverse(result)或者用LinkedList的addFirst方法。面试官如何实现锯齿形层序遍历Zigzag你用一个布尔变量标记方向奇数层正常添加偶数层用add(0, val)或Collections.reverse。面试官这道题和“二叉树的右视图”有什么关系你右视图就是每层的最后一个节点。可以在内层循环结束后取currentLevel.get(currentLevel.size()-1)。面试官能否用 DFS 实现优缺点是什么你可以DFS 需传递深度参数。优点是代码短缺点是空间复杂度 O(h)且不符合 BFS 的直观性。十一、这道算法题在实际开发中的应用1. 文件系统目录遍历ls -R或tree命令的底层实现按层级展示目录结构便于用户理解嵌套关系2. Web 爬虫广度优先爬取从种子 URL 开始逐层爬取链接BFS 保证先爬取浅层页面符合“重要页面优先”原则3. 社交网络好友推荐从用户出发逐层扩展好友的好友第1层直接好友第2层好友的好友依此类推4. 游戏 AI关卡探索迷宫寻路中BFS 找到最短路径层序遍历用于可视化探索过程5. 编译器语法树遍历按层级分析代码结构顶层程序中层函数底层表达式6. 数据库索引B树遍历虽然 B树非二叉但层序遍历思想类似用于调试和可视化索引结构十二、相关题目推荐掌握本题后可挑战以下进阶题目题号题目关联点107二叉树的层序遍历 II自底向上103二叉树的锯齿形层序遍历之字形199二叉树的右视图每层最后一个637二叉树的层平均值每层统计515在每个树行中找最大值每层最值116填充每个节点的下一个右侧节点指针层内连接117填充每个节点的下一个右侧节点指针 II非完美二叉树重点推荐第103题在层序遍历基础上增加方向控制考察细节处理。第199题层序遍历的典型应用高频面试题。十三、总结与延伸核心收获BFS 的精髓队列实现 FIFOsize()技巧实现分层循环不变式保证正确性层级控制的重要性普通 BFS ≠ 层序遍历必须显式处理层边界空间复杂度的权衡BFS 空间 ∝ 宽度DFS 空间 ∝ 高度根据树形状选择模板化思维层序遍历是解决“按层处理”问题的通用模板稍作修改即可应对多种变体延伸思考N 叉树的层序遍历只需将left/right改为遍历children列表。带权 BFS若边有权重层序遍历不再适用需用 Dijkstra 算法。分布式层序遍历在大规模图如社交网络中需设计分布式 BFS 算法。内存受限场景可用迭代加深IDDFS模拟 BFS但时间复杂度升至 O(n²)。最后建议面试准备务必手写 BFS 层序遍历并能解释levelSize的作用。工程实践优先使用ArrayDeque注意边界条件。算法竞赛熟练掌握各种层序遍历变体它们是高频考点。结语二叉树的层序遍历是算法学习的里程碑——它简单却深刻基础却强大。掌握它你就掌握了 BFS 的灵魂也为解决更复杂的树和图问题打下了坚实基础。愿你在编程路上既能写出优雅代码也能洞察算法之美。欢迎点赞、收藏、评论交流你的支持是我持续输出高质量内容的动力