二叉树层序遍历是算法面试中的高频考点,也是理解树结构操作的基础。今天我将分享两种Java实现方式:基于队列的迭代法和基于递归的深度优先搜索法。这两种方法各有特点,适用于不同场景。
层序遍历(Level Order Traversal)是指按照树的层级从上到下、从左到右依次访问每个节点。想象一下,这就像是在读取一本书的目录结构:先看第一章标题,然后是第一章下的第一节、第二节,接着是第二章标题,依此类推。
这种遍历方式在以下场景特别有用:
迭代法使用队列(Queue)这种先进先出(FIFO)的数据结构来实现层序遍历。基本思路是:
java复制public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while(!queue.isEmpty()) {
int len = queue.size();
List<Integer> levelNodes = new ArrayList<>();
while(len > 0) {
TreeNode node = queue.poll();
levelNodes.add(node.val);
if(node.left != null) {
queue.offer(node.left);
}
if(node.right != null) {
queue.offer(node.right);
}
len--;
}
res.add(levelNodes);
}
return res;
}
队列选择:这里使用了ArrayDeque作为队列实现,相比LinkedList有更好的空间局部性和更少的内存开销。在Java中,ArrayDeque通常是实现队列的首选。
层级处理:内层循环通过len变量确保只处理当前层的节点,这是关键点。每次外层循环开始时,队列中只包含当前层的所有节点。
空值检查:在访问左右子节点前进行非空检查,避免将null值加入队列。
时间复杂度:O(n),每个节点被访问一次
空间复杂度:O(n),最坏情况下队列需要存储最后一层的所有节点(对于完全二叉树约为n/2)
提示:在实际面试中,建议明确说明时间和空间复杂度。对于二叉树问题,通常n表示节点总数。
递归法采用深度优先搜索(DFS)的策略,但通过记录层级信息来实现层序遍历的效果。核心思想是:
java复制class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
dfs(root, 0);
return res;
}
private void dfs(TreeNode node, int level) {
if(node == null) {
return;
}
if(res.size() <= level) {
res.add(new ArrayList<>());
}
res.get(level).add(node.val);
dfs(node.left, level+1);
dfs(node.right, level+1);
}
}
层级控制:通过level参数跟踪当前递归的深度,确保节点值被放入正确的层级列表。
列表初始化:当访问到新层级时(res.size() <= level),需要为该层级初始化一个新的列表。
静态方法限制:注意dfs方法不能声明为static,因为它需要访问实例变量res。这是Java中内部类访问外部类变量的常见问题。
遍历顺序:虽然是DFS,但由于我们按照层级组织结果,最终效果等同于BFS的层序遍历。
时间复杂度:O(n),每个节点被访问一次
空间复杂度:O(h),h为树的高度(递归调用栈的深度)
| 特性 | 迭代法(队列) | 递归法(DFS) |
|---|---|---|
| 时间复杂度 | O(n) | O(n) |
| 空间复杂度 | O(n) | O(h) |
| 代码简洁度 | 中等 | 更简洁 |
| 适用场景 | 通用 | 树较平衡时更好 |
优先使用迭代法:在大多数情况下,迭代法是更安全的选择,特别是当树的深度很大时(避免栈溢出)。
考虑递归法:当代码简洁性更重要,且能确定树不会太深时(如平衡二叉树),递归法更优雅。
特殊场景:如果需要同时进行其他DFS操作,递归法可能更合适,因为它天然支持深度优先的访问顺序。
队列处理错误:
int len = queue.size()并在内层循环中递减空指针异常:
node.left和node.right前进行非空检查递归栈溢出:
打印中间结果:在关键步骤打印队列状态或递归层级,帮助理解程序执行流程。
可视化小树:用简单的3层二叉树手动模拟算法执行,验证边界条件。
单元测试:为以下情况编写测试用例:
掌握了基本层序遍历后,可以尝试解决以下变种问题:
锯齿形层序遍历:奇数层从左到右,偶数层从右到左
层平均值计算:计算每一层节点的平均值
右视图/左视图:只返回每层最右/最左的节点
N叉树的层序遍历:节点可能有多个子节点
在实际开发中,层序遍历的思想还可以应用于:
理解这些基础算法不仅有助于通过技术面试,更能培养解决复杂问题的思维方式。建议读者动手实现这些算法,并尝试解决相关的LeetCode题目(如102、107、103、199等)来巩固学习效果。