算法思想:
- 层序遍历是指从根节点开始,按层次逐层遍历二叉树的所有节点。每一层从左到右依次访问节点,然后再进入下一层。
- 使用队列这种数据结构来实现层序遍历,因为队列遵循**先进先出(FIFO)**的原则,正好适合处理逐层的遍历过程。
- 主要思路是:
- 从根节点开始,将它加入队列。
- 逐层访问队列中的每个节点,并将该节点的子节点(如果有的话)加入到队列中。
- 每次完成一层的遍历后,将这一层的所有节点值存储到结果列表中,继续处理下一层。
java
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) {
return result;
}
queue.offer(root);
while(!queue.isEmpty()) {
int currentLevelnum = queue.size(); //当前层需要处理的节点个数
List<Integer> currentLevel = new ArrayList<>(); //初始化当前层节点不能放在while循环外部!!
for(int i = 0; i < currentLevelnum; ++i) {
TreeNode node = queue.poll(); //这里只是出队了,而出队节点在二叉树中的结构并未改变。
currentLevel.add(node.val);
if(node.left != null) {
queue.offer(node.left);
}
if(node.right != null) {
queue.offer(node.right);
}
}
result.add(currentLevel);
}
return result;
}
}
为什么List<Integer> currentLevel = new ArrayList<>();
的位置放到while循环外部,会得到错误的结果?
来分析一下为什么会这样。
问题的根源:
currentLevel
的复用问题 :在你的代码中,currentLevel
只在方法开始时被初始化一次,然后在后续的每一层遍历中,所有层的节点值都会被添加到同一个currentLevel
列表中。- 因为你在
while
循环外部初始化了currentLevel
,当你进行多层遍历时,所有层的节点值都会被不断添加到同一个currentLevel
列表,而不是为每一层创建新的列表。 - 当你将
currentLevel
添加到result
中时,实际上你添加的是同一个currentLevel
的引用。即使在后续层继续向这个列表添加元素,result
中的所有层都会指向同一个列表,最终导致所有层的结果被错误地合并在一起。
- 因为你在
具体错误结果的表现:
- 假设你有一个三层的树结构。在第一次遍历第一层时,
currentLevel
里保存的是第一层的节点值,例如[3]
。 - 然后遍历第二层时,第二层的节点值(如
[9, 20]
)也被添加到了同一个currentLevel
中,因此currentLevel
变成[3, 9, 20]
。 - 最后,第三层的节点值(如
[15, 7]
)也被添加到了currentLevel
,使得currentLevel
变成[3, 9, 20, 15, 7]
。 - 最终,
result
中的所有子列表都指向同一个currentLevel
,导致result
中的每一层显示的都是完整的[3, 9, 20, 15, 7]
,而不是分层的结果。
正确的做法:
要避免这个问题,需要在每次开始遍历新的一层时,创建一个新的 currentLevel
列表。这样,每一层的节点值都会存储在独立的列表中。具体修改如下:
java
public class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
// 在每一层遍历开始时,重新初始化 currentLevel
List<Integer> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode currentNode = queue.poll();
currentLevel.add(currentNode.val);
if (currentNode.left != null) {
queue.offer(currentNode.left);
}
if (currentNode.right != null) {
queue.offer(currentNode.right);
}
}
result.add(currentLevel); // 添加当前层的节点值到结果中
}
return result;
}
}
解释修改后的代码:
-
List<Integer> currentLevel = new ArrayList<>();
放在while
循环内部:- 每次进入
while
循环(即开始遍历新的层级节点)时,都会创建一个新的currentLevel
列表,用来存储该层的节点值。 - 这样可以保证每一层的节点值都保存在独立的列表中,不会影响其他层的结果。
- 每次进入
-
result.add(currentLevel);
正确地添加当前层的结果:- 每一层遍历完成后,将当前层的节点值列表
currentLevel
添加到result
中。 - 由于每次都是一个新的
currentLevel
,因此不会有多个层共享同一个列表引用,最终的result
会包含每一层的正确结果。
- 每一层遍历完成后,将当前层的节点值列表
结论:
这个问题的核心是你需要为每一层创建一个新的 currentLevel
,而不是在所有层之间共享一个 currentLevel
。在每次开始遍历新的一层时,初始化一个新的 currentLevel
,可以保证每一层的节点值都被正确地存储和输出。