1 二叉树的层序遍历
上一期我们讲了关于二叉树的前序、中序以及后序遍历的相关内容。然而,还存在一种遍历方式,这种方式非常符合我们人类的正常思维,可以求解很多树相关的问题,比较暴力——二叉树的层序遍历。
二叉树的层序遍历与前中后序遍历分别对应于图论中的广度优先和深度优先搜索,大家可以好好体会深度和广度这两个词。画个图来增强理解:
像左图一层一层的遍历就是广度优先,类似于二叉树的层序遍历,像右边这种,一个方向一个方向的遍历就是深度优先,类似于我们的前中后序遍历。
2 解法套路
二叉树的层序遍历,有非常明显的套路。102. 二叉树的层序遍历,以这道题为例,先贴代码:
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<TreeNode> deque = new LinkedList();
deque.push(root);
while (!deque.isEmpty()) {
int size = deque.size();
List<Integer> path = new ArrayList<>();
while (size-- > 0) {
TreeNode p = deque.pollFirst();
path.add(p.val);
if (p.left != null) {
deque.addLast(p.left);
}
if (p.right != null) {
deque.addLast(p.right);
}
}
res.add(path);
}
return res;
}
(1)定义一个队列,先把树的头节点放入队列头。
(2)定义一个循环,如果队列不为空,就一直循环下去。
(3)求队列的长度,这个长度代表了树当前层的节点数。
(4)然后,开始从队列中弹出所有元素,每弹出一个元素,就把他的左节点和右节点加入到队列的尾部,当这一层所有节点都被弹完后,是不是下一层就重新填充了队列。
(5)直到最后一层。
3 相关例题
3.1 二叉树的层序遍历Ⅱ
107. 二叉树的层序遍历 II
给你二叉树的根节点 root
,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<TreeNode> deque = new LinkedList<>();
deque.push(root);
while (!deque.isEmpty()) {
int size = deque.size();
List<Integer> list = new ArrayList<>();
while (size-- > 0) {
TreeNode node = deque.pollFirst();
list.add(node.val);
if (node.left != null) {
deque.addLast(node.left);
}
if (node.right != null) {
deque.addLast(node.right);
}
}
res.add(0, list);
}
return res;
}
这道题和上面的例题,几乎完全相同,只不过是从下往上输出。那我们可以利用List列表的特性,每次都在0的位置add,最后展示的时候就是从下往上。
3.2 二叉树的层平均值
637. 二叉树的层平均值
给定一个非空二叉树的根节点 root
, 以数组的形式返回每一层节点的平均值。与实际答案相差 以内的答案可以被接受。(这句话告诉我们定义sum的时候要用double)
public List<Double> averageOfLevels(TreeNode root) {
List<Double> res = new ArrayList<>();
Deque<TreeNode> deque = new LinkedList();
deque.push(root);
while (!deque.isEmpty()) {
int size = deque.size();
Double sum = 0d;
int nums = size;
while (size-- > 0) {
TreeNode pop = deque.pop();
sum += pop.val;
if (pop.left != null) {
deque.addLast(pop.left);
}
if (pop.right != null) {
deque.addLast(pop.right);
}
}
res.add(sum / nums);
}
return res;
}
这道题用层序做非常简单,但是也可以用前序遍历去做,大家可以思考一下,怎么用前序做,下一期我会给出详解。
3.3 N叉树的层序遍历
429. N 叉树的层序遍历
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null 值分隔。
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<Node> deque = new LinkedList<>();
deque.push(root);
while (!deque.isEmpty()) {
int size = deque.size();
List<Integer> list = new ArrayList<>();
while (size-- > 0) {
Node pop = deque.pop();
list.add(pop.val);
if (pop.children != null) {
for (Node child : pop.children) {
deque.addLast(child);
}
}
}
res.add(list);
}
return res;
}
万变不离其宗,二叉树都是左节点、右节点,N叉树是一个节点下面挂了一个列表,列表中装着它的孩子节点,与二叉树对应,那就每次队列弹出这个节点的时候就把它的孩子节点列表遍历了,都从队列尾部装进去就好了。
3.4 二叉树层最大值
给定一棵二叉树的根节点 root
,请找出该二叉树中每一层的最大值。
public List<Integer> largestValues(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<TreeNode> deque = new LinkedList<>();
deque.push(root);
while (!deque.isEmpty()) {
int size = deque.size();
int maxValue = deque.peek().val;
while (size-- > 0) {
TreeNode pop = deque.pop();
maxValue = Math.max(maxValue, pop.val);
if (pop.left != null) {
deque.addLast(pop.left);
}
if (pop.right != null) {
deque.addLast(pop.right);
}
}
res.add(maxValue);
}
return res;
}
这道题同样可以用前序遍历去做,但是层序比较容易想,且时间复杂度和前序都是O(N),所以还是建议大家用层序去做。下一期会出前序解法。、
4 总结
二叉树的层序遍历套路非常明显,可以解决很多树的问题,不过用层序解答的结果就是时间复杂度都是O(N),所以层序遍历适合那些无论如何都要遍历整棵树的题,例如,求层平均值、求层最大值、求二叉树最大深度、求二叉树最小深度,这些题是你要把整棵树遍历完才能知道答案的,但是用前中后序(深度优先)遍历也可以解决,且时间复杂度也是O(N),只不过相比之下可能层序更容易想。