102. 二叉树的层次遍历(中等)
题目描述
给定一个二叉树, 返回其层次遍历序列的二维数组.
比如说,给你的二叉树是:
1
/ \
2 4
/ \
8 16
它的层序遍历结果是:
[
[1],
[2, 4],
[8, 16]
]
题目解析
层次遍历(层序遍历)考点是BFS的使用, 所以使用辅助队列来完成, 不同于其他遍历, 层序遍历需要使用BFS(广度优先搜索)的方法, 因此我们使用队列.
当然, 也可用DFS来进行实现, 每一次递归中把当前的层号(level)传入即可.
解法一: DFS
DFS不难, 关键是记录每一次递归的深度(level), 每次对应放进去即可.
// DFS递归解法
public void DFS(TreeNode root, int level, List<List<Integer>> result) {
if (root == null) return;
if (result.size() <= level) {
result.add(new ArrayList<>());
}
// 关键点, 无论遍历到的顺序如何, 只要把当前节点值放到对应层次的列表中即可.
result.get(level).add(root.val);
DFS(root.left, level + 1, result);
DFS(root.right, level + 1, result);
}
public List<List<Integer>> levelOrderRecursive(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
DFS(root, 0, result);
return result;
}
解法二: BFS队列
使用BFS的核心问题就是如何知道当前节点的层数, 使用前一种方法(DFS), 由于本身有递归深度, 就不需要考虑这个问题.
对于这道题, 有两种方式解决节点的层数问题:
方式一: 传统方式
对于传统方式, 我们又有两种方法进行遍历:
第一种: 使用新的class(结构体)
解法非常简单, 使用class重新封装, 加一个新的字段(level)即可.
注意点: 判空时需要判断s.node是否为空.
// BFS队列解法一(使用新的class)
class Node {
Node(TreeNode node, int level) {
this.node = node;
this.level = level;
}
TreeNode node;
int level;
}
public List<List<Integer>> levelOrderIterative1(TreeNode root) {
Queue<Node> queue = new LinkedList<>();
List<List<Integer>> ans = new ArrayList<>();
queue.add(new Node(root, 0));
while (!queue.isEmpty()) {
Node s = queue.poll();
// 这里需要判断s.node是否为空, 而不是s是否为空
if (s.node != null) {
int curLevel = s.level;
if (ans.size() <= curLevel) {
ans.add(new ArrayList<Integer>());
}
ans.get(curLevel).add(s.node.val);
queue.offer(new Node(s.node.left, s.level + 1));
queue.offer(new Node(s.node.right, s.level + 1));
}
}
return ans;
}
第二种: 使用辅助队列同步更新层序
这种方法的思路就是用一个辅助队列, 来同步记录节点所在的层序号, 免去了定义一个新的class的麻烦.
// BFS解法二: 使用辅助队列同步更新层序的值
public List<List<Integer>> levelOrderIterative2(TreeNode root) {
List<List<Integer>> ans = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
Queue<Integer> levelQueue = new LinkedList<>();
queue.offer(root);
int curLevel = 0;
levelQueue.offer(curLevel);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
curLevel = levelQueue.poll();
if (node != null) {
if (ans.size() <= curLevel) {
ans.add(new ArrayList<Integer>());
}
ans.get(curLevel).add(node.val);
queue.offer(node.left);
levelQueue.offer(curLevel + 1);
queue.offer(node.right);
levelQueue.offer(curLevel + 1);
}
}
return ans;
}
方式二: 使用for循环
在while循环中, 用一个for循环把当前队列中的所有节点拿出来(每次拿出来就是当前层的所有节点), 并依次把左右节点放入队列中.
注意有一个坑: 使用队列时, 如果需要用到队列大小, 需要提前确定下来.
否则在程序执行过程中会动态被改变.
// BFS队列解法
public List<List<Integer>> levelOrderIterative(TreeNode root) {
if (root == null) {
return new ArrayList<>();
}
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> result = new ArrayList<>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> level = new ArrayList<>();
// 坑, 需要提前把queue的大小确定下来, 否则会动态改变
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
result.add(level);
}
return result;
}