层次遍历
对于层次遍历,我们可以用队列先进先出的特点,在出列的时候将左右子树放入队列中,这样出列的顺序就是树每一层的顺序
基本的层次遍历
2.0 如何确定底基层有哪些节点
我们在遍历队列时,当前队列的长度就是该层的节点,不会出现节点中出现下一层节点的情况,因为每一次循环我们会将该层的所有节点全部抛出。
2.1 层次遍历
LeetCode102 题目要求:给你一个二叉树,请你返回其按层序遍历得到的节点值。(即逐层地,从左到右访问所有节点)。
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
代码如下:
public static List<List<Integer>> level102Order(TreeNode root) {
if (root == null) {
return new ArrayList<List<Integer>>();
}
List<List<Integer>> res = new ArrayList<List<Integer>>();
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
//将根节点放入队列中,然后不断遍历队列
queue.add(root);
while (queue.size() > 0) {
//获取当前队列的长度,这个长度相当于 当前这一层的节点个数
int size = queue.size();
ArrayList<Integer> tmp = new ArrayList<Integer>();
//将队列中的元素都拿出来(也就是获取这一层的节点),放到临时list中
//如果节点的左/右子树不为空,也放入队列中
for (int i = 0; i < size; ++i) {
TreeNode t = queue.remove();
tmp.add(t.val);
if (t.left != null) {
queue.add(t.left);
}
if (t.right != null) {
queue.add(t.right);
}
}
//将临时list加入最终返回结果中
res.add(tmp);
}
return res;
}
这里的SIZE就是确定了该层的节点数,这样就算后面将新的子节点插入,也能限制防止多抛。
2.2 自底向上
LeetCode 107.给定一个二叉树,返回其节点值自底向上的层序遍历。(即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。例如给定的二叉树为:
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]
本题相比于基本的层次遍历,我们只需要将结果用头插法的方式插入结果即可。且通过链表结构插入的话,插入头插法的时间复杂度为O(1)。
代码如下:
public static List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> levelOrder = new LinkedList<List<Integer>>();
if (root == null) {
return levelOrder;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> level = new ArrayList<Integer>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);
TreeNode left = node.left, right = node.right;
if (left != null) {
queue.offer(left);
}
if (right != null) {
queue.offer(right);
}
}
levelOrder.add(0, level);//栈
}
return levelOrder;
}
2.3 二叉树的锯齿形层序遍历
LeetCode103 题,要求是:给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]
- 加个判断,如果是true则正序插入输出,否则是逆序插入输出。isOrderLeft
- 我们需要通过双端队列存入每一层的结果 Deque levelList = new LinkedList();
代码如下:
public static List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ans = new LinkedList<List<Integer>>();
if (root == null) {
return ans;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
boolean isOrderLeft = true;
while (!queue.isEmpty()) {
Deque<Integer> levelList = new LinkedList<Integer>();
int size = queue.size();
for (int i = 0; i < size; ++i) {
TreeNode curNode = queue.poll();
if (isOrderLeft) {
levelList.offerLast(curNode.val);
} else {
levelList.offerFirst(curNode.val);
}
if (curNode.left != null) {
queue.offer(curNode.left);
}
if (curNode.right != null) {
queue.offer(curNode.right);
}
}
ans.add(new LinkedList<Integer>(levelList));
isOrderLeft = !isOrderLeft;
}
return ans;
}
2.4 N叉树的层次遍历
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]
- 左右孩子变成了一个列表。所以我们由添加左右孩子 -> 对列表进行循环添加。
- 通过另一个队列来记录弹出后节点的子列表,不用通过SIZE去记录改成的数据。当然,通过SIZE也是一样的效果。
代码如下:
public static List<List<Integer>> nLevelOrder(NTreeNode root) {
List<List<Integer>> value = new ArrayList<>();
Deque<NTreeNode> q = new ArrayDeque<>();
if (root != null)
q.addLast(root);
while (!q.isEmpty()) {
Deque<NTreeNode> next = new ArrayDeque<>();
List<Integer> nd = new ArrayList<>();
while (!q.isEmpty()) {
NTreeNode cur = q.pollFirst();
nd.add(cur.val);
for (NTreeNode chd : cur.children) {
if (chd != null)
next.add(chd);
}
}
q = next;
value.add(nd);
}
return value;
}
处理每层元素的节点
3.0 基本原理
我们先看接下来的几题题目的特征:
- 每个树行中寻找最大值
- 每个树行中寻找平均值
- 二叉树的右视图
- 最底层最左边
可以发现,是对每一层元素进行判断,修改等。所以只要在遍历每层元素的时候添加相对应的逻辑判断即可。
3.1 每个树行中寻找最大值
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]
只要在层次遍历的时候记录每层的最大值即可
代码如下:
public static List<Integer> largestValues(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> deque = new ArrayDeque<>();
if (root != null) {
deque.addLast(root);
}
while (!deque.isEmpty()) {
int size = deque.size();
int levelMaxNum = Integer.MIN_VALUE;
for (int i = 0; i < size; i++) {
TreeNode node = deque.poll();
levelMaxNum = Math.max(node.val, levelMaxNum);
if (node.left != null) deque.addLast(node.left);
if (node.right != null) deque.addLast(node.right);
}
res.add(levelMaxNum);
}
return res;
}
3.2 每个树行中寻找平均值
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值
输入:root = [3,9,20,null,null,15,7]
输出:[3.00000,14.50000,11.00000] 解释:第 0 层的平均值为 3,第 1 层的平均值为 14.5,第 2 层的平均值为 11 。 因此返回 [3, 14.5, 11] 。
与上题类似,只需将最大值的判断换为取平均值即可:
代码如下:
public static List<Double> averageOfLevels(TreeNode root) {
List<Double> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> list = new LinkedList<>();
list.add(root);
while (list.size() != 0) {
int len = list.size();
double sum = 0;
for (int i = 0; i < len; i++) {
TreeNode node = list.poll();
sum += node.val;
if (node.left != null) list.add(node.left);
if (node.right != null) list.add(node.right);
}
res.add(sum / len);
}
return res;
}