该部分内容主要针对树层序遍历这个高频考点展开,细致深入的分析树层序遍历的模板,以及应用模板解决变式问题的思路,仔细总结复盘就可以掌握树层序遍历部分的出题方向,你也能成为出算法题的大牛,给自己出题,给自己解答!(独孤求败)
1.树层次遍历
树的层次遍历,掌握模板即可,模板里面最重要的是什么?答:队列
关键点在于掌握队列和树节点入队出队的逻辑:逻辑为队头元素出队并保存出队元素的元素值,该元素的左右孩子节点依次入队,如果左右孩子为空则不入队,直到队列为空层序遍历结束。
厘清思路,直接上代码!
public static List<Integer> simpleLevelOrder(TreeNode root) {
if(root == null){
return new ArrayList<Integer>();
}
Queue<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> res = new ArrayList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode curNode = queue.poll();
res.add(curNode.val);
if(curNode.left != null){
queue.add(curNode.left);
}
if(curNode.right != null){
queue.add(curNode.right);
}
}
return res;
}
2.二叉树从上至下逐层遍历
该问题是树层序遍历的第一类变式问题,该问题主要考察是否能在层序遍历中找到每层的树节点。这个问题的关键点是在于怎样标记树每层末尾节点。可以使用一个变量size来记录本层到底有多少个节点,当size==0时,将本层的节点放到一个数组中进行保存,层次遍历的整体逻辑是逐层遍历!直接上代码,更易于理解!
public static List<List<Integer>> mylLevel102Order(TreeNode root) {
if(root == null){
return new ArrayList<List<Integer>>();
}
Queue<TreeNode> queue = new LinkedList<>();
ArrayList<List<Integer>> res = new ArrayList<>();
queue.offer(root);
ArrayList<Integer> ansLevel = new ArrayList<>();
int size = 0;
while(!queue.isEmpty()){
size = queue.size();
ArrayList<Integer> tmp = new ArrayList<>();
//遍历第每层
for(int i = 0; i < size; i++){
TreeNode curNode = queue.poll();
tmp.add(curNode.val);
if(curNode.left!=null){
queue.add(curNode.left);
}
if(curNode.right!=null){
queue.add(curNode.right);
}
}
res.add(tmp);
}
return res;
}
3.二叉树从下至上层序遍历
该问题是针对问题2的变式,在问题2中我们已经能够逐层获得树节点信息,而问题三考察的点在于如何把每一层的树节点信息逆序存储起来。我猜你已经想到了!“逆向过程”可以使用栈或头插法。
没错两种方法都可以,这里我们采用头插法进行!直接上代码!
public static List<List<Integer>> myLevelOrderBottom(TreeNode root) {
if(root == null){
return new LinkedList<List<Integer>>();
}
LinkedList<List<Integer>> res = new LinkedList<>();
LinkedList<TreeNode> queue = new LinkedList<>();
int size = 0;
queue.add(root);
while(!queue.isEmpty()){
size = queue.size();
ArrayList<Integer> tmp = new ArrayList<>();
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);
}
}
res.addFirst(tmp);
}
return res;
}
4.二叉树的锯齿形层序遍历
什么是锯齿形层序遍历?就是蛇形层序遍历。那我懂了,偶数层正向输出节点,奇数层反向输出节点。什么数据结构既能顺序输出,又能逆序输出?答:对端队列
还是套用层序遍历模板,采用双端队列存储每层元素,当偶数层正常入队,奇数层从头部入队,即可完成锯齿形层序遍历。厘清逻辑,直接上代码!
public static List<List<Integer>> myZigzagLevelOrder(TreeNode root) {
ArrayList<List<Integer>> res = new ArrayList<>();
if(root == null){
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean isEven = true;//是否为偶数层
int size = 0;
while(!queue.isEmpty()){
size = queue.size();
Deque<Integer> levelList = new LinkedList<>();
for (int i = 0; i < size; i++) {
TreeNode t = queue.poll();
if(isEven){
levelList.offerLast(t.val);
}
if(!isEven){
levelList.offerFirst(t.val);
}
if(t.left != null){
queue.offer(t.left);
}
if(t.right != null){
queue.offer(t.right);
}
}
res.add(new ArrayList<Integer>(levelList));
isEven = !isEven;
}
return res;
5.N叉树层序遍历
N叉树层序遍历有什么难度嘛?Paper tiger!只不过把左右孩子入队变成孩子节点全部入队了,倘若我能将孩子节点“正向存储(从左至右)”和“逆向存储(从右至左)”那么依然可以变幻出诸如,N叉树锯齿形层序遍历的问题,可以多思考一下,大有裨益!厘清思路,直接上代码!
注意,我们此时没有使用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;
}
6.树每层元素的问题
我们知道树每层元素存储到一个数组中,数组头部存储该层最左侧树节点,数组尾部存储该层最右侧树节点,依然可以使用一个辅助变量max或min记录本层树节点的最大值,亦可求解平均值。因此,该类问题的变式问题分成,树每层最大(小)值,树每层平均值,树左(右)视图三类问题!
树每层最大值
经过上面的分析,可以直接上代码!
public static List<Integer> myLargestValues(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
Queue<TreeNode> next = new LinkedList<>();//下一层节点
int max = Integer.MIN_VALUE;
while(!queue.isEmpty()){
TreeNode t = queue.poll();
max = Math.max(t.val, max);
if(t.left!=null){
next.offer(t.left);
}
if(t.right!=null){
next.offer(t.right);
}
}
res.add(max);
queue = next;
}
return res;
}
树每层平均值
经过上面的分析,可以直接上代码!
public static List<Double> myAverageOfLevels(TreeNode root) {
ArrayList<Double> res = new ArrayList<>();
if(root == null){
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int size = 0;
while(!queue.isEmpty()){
int sum = 0;
size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode t = queue.poll();
sum += t.val;
if(t.left!=null){
queue.offer(t.left);
}
if(t.right!=null){
queue.offer(t.right);
}
}
res.add(1.0 * sum / size);
}
return res;
}
树右视图
经过上面的分析,可以直接上代码!
public static List<Integer> myRightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int size = 0;
while (!queue.isEmpty()) {
size = queue.size();
int rightNodeVal = 0;
for (int i = 0; i < size; i++) {
TreeNode t = queue.poll();
if(t.left != null) queue.offer(t.left);
if(t.right != null) queue.offer(t.right);
if(i == size - 1) rightNodeVal = t.val;
}
res.add(rightNodeVal);
}
return res;
}
OK,《算法通关村第六关——树层序遍历白银挑战笔记》结束,喜欢的朋友三联加关注!关注鱼市带给你不一样的算法小感悟!(幻听)
再次,感谢鱼骨头教官的学习路线!鱼皮的宣传!小y的陪伴!ok,拜拜,第七关第一幕见!
很难坚持下来,可坚持下来了,也就不觉得难!加油!今天化身杰迷,去看了张杰长沙演唱会!虽然在场外,让我想起了五年前在杭州,依旧是场外听张杰唱歌的场景!期待下一次演唱会,我又会是什么样子!于2023年7月29日!