前言
博主最近在刷leetcode ,做到二叉树套题的时候发现很多题的解题思路都是基于二叉树的层序遍历来完成的,因此写下这篇文章,记录一下二叉树层序遍历这件"神器"在实战的运用。
[leetcode] 102.二叉树的层序遍历
leetcode题目链接
二叉树的层序遍历与传统的前序、中序、后序遍历都有一些区别,他是按层级、从左到右、从上到下进行遍历的,因此当我在遍历当前层节点的时候,肯定需要记录当前层所有节点的left 、right ,保存到队列中,进行下一轮遍历,直到节点没有left 、right ,则代表已经遍历到了最后一层了。
因此需要借助一个辅助数据结构——队列 ,队列先进后出,符合层序遍历的顺序性,其实此题就是队列 + 广度优先遍历 的一道结合题。
直接看代码吧:
var levelOrder = function(root) {
const res = [], queue = [];
queue.push(root);
if(root === null) return res;
while(queue.length !== 0) {
let level = [];
const length = queue.length
for(var i = 0; i < length; i++) {
var node = queue.shift();
level.push(node.val);
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
res.push(level);
}
return res;
};
接下来我们逐行分析代码。
- 首先定义了一个结果和一个队列,对应
res 和queue ,将顶层的root 节点加入到队列中,开启循环。 - 在每一轮
while 循环中,我们从左到右依次取出节点(shift api )并且判断每个节点下一层是否有后代(left、right )的判断,如果有,则加入到队列中。 const length = queue.length 记录了队列在每一层遍历开始时的最初状态,保证了后面的for 循环遍历的内容是当前层的节点,不会因为left、right 加入到队列中的节点影响到当前层的循环轮数。- 最终,队列中所有节点都遍历完毕,在
for 循环中也没有发现新的下层节点,循环结束,返回结果。
此时我们就掌握了二叉树的层序遍历了,那么如下九道力扣上的题目,只需要修改模板的两三行代码(不能再多了),便可打倒!你真的会发现,理解了层序遍历后,解决这些关联题,会如鱼得水一般简单
- 102.二叉树的层序遍历
- 107.二叉树的层次遍历II
- 199.二叉树的右视图
- 637.二叉树的层平均值
- 429.N叉树的前序遍历
- 515.在每个树行中找最大值
- 116.填充每个节点的下一个右侧节点指针
- 117.填充每个节点的下一个右侧节点指针II
- 104.二叉树的最大深度
- 111.二叉树的最小深度
[leetcode] 107.二叉树的层序遍历II
leetcode题目链接
此题与102.二叉树的层序遍历 极其相似,只需要把最后的res 结果的排列顺序改变一下即可,代码架构和102完全一样。
代码:
var levelOrderBottom = function(root) {
var res = [], queue = [];
queue.push(root);
if(root === null) return res;
while(queue.length) {
let length = queue.length;
const level = [];
for(var i = 0; i < length; i++) {
var node = queue.shift();
level.push(node.val)
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
res.unshift(level);
}
return res;
};
[leetcode] 199.二叉树的右视图
leetcode题目链接
此题从题目描述中可以看到,需要收集每一层的最后一个节点,有了"神器"的你,此时已经有思路了吧?这不是只需要在每一轮while 循环中的for 里加一个判断条件,取出最后一个节点就好了?上代码!
代码:
var rightSideView = function(root) {
var res = [], queue = [];
queue.push(root);
if(root === null ) return res;
while(queue.length !== 0 ){
const length = queue.length;
for(var i = 0; i < length; i++) {
var node = queue.shift();
if(i === length - 1) {
res.push(node.val);
}
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
return res;
};
[leetcode] 637.二叉树的层平均值
leetcode题目链接
此题只需要在每层节点取完以后,对节点的平均值进行一个计算即可,相比于前面几题,区别在收集的返回结果不一样,解题代码架构没有区别。
代码:
var averageOfLevels = function(root) {
var res = [], queue = [];
queue.push(root);
if(root === null) return res;
while(queue.length !== 0) {
const length = queue.length;
let total = 0;
for(var i = 0; i < length; i++) {
let node = queue.shift();
total += node.val;
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
res.push(total / length);
}
return res;
};
[leetcode] 429.N叉树的层序遍历
leetcode题目链接
此题会有一点小弯需要绕一下,首先数据结构不同,TreeNode 节点是这样的:
也就是说我们在每一层节点的循环中,不是再去收集节点的left 、right 了,而是去遍历节点的children ,将children 中的节点加入到队列中。
代码:
var levelOrder = function(root) {
var res = [], queue = [];
queue.push(root);
if(root === null) return res;
while(queue.length !== 0) {
var level = [];
var length = queue.length;
for(var i = 0; i < length; i++) {
var node = queue.shift();
level.push(node.val)
for(var item of node.children) {
item && queue.push(item);
}
}
res.push(level);
}
return res;
};
[leetcode] 515. 在每个树行中找最大值
leetcode题目链接
此题类似于637.二叉树的层平均值 ,只是每一层收集的内容变成了最大值。
代码:
var largestValues = function(root) {
var res = [], queue = [];
queue.push(root);
if(root === null) return res;
while(queue.length !== 0) {
const length = queue.length;
const list = [];
for(var i = 0; i < length; i++) {
var node = queue.shift();
list.push(node.val);
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
res.push(Math.max(...list));
}
return res;
};
[leetcode] 116. 填充每个节点的下一个右侧节点指针
leetcode题目链接
此题无需重新组装新的返回内容,只需要重新组建root 中的每一个节点即可,每一个TreeNode 默认的next 为null ,根据题目要求,在每一层所有节点,我们对除了最右节点以外的所有节点添加一个next 属性即可,根据队列先进先出的原则,next 的值就是queue[0] ,也就是队列的首项。
代码:
var connect = function(root) {
var queue = [root];
if(root === null) return root;
while(queue.length) {
const length = queue.length;
for(var i = 0; i < length; i++) {
var node = queue.shift();
if(i < length - 1) {
node.next = queue[0];
}
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
return root;
};
[leetcode] 117. 填充每个节点的下一个右侧节点指针II
leetcode题目链接
此题与116. 填充每个节点的下一个右侧节点指针 类似,直接上代码。
代码:
var connect = function(root) {
if (root === null) {
return null;
}
let queue = [root];
while (queue.length > 0) {
let n = queue.length;
for (let i=0; i<n; i++) {
let node = queue.shift();
if (i < n-1) node.next = queue[0];
if (node.left != null) queue.push(node.left);
if (node.right != null) queue.push(node.right);
}
}
return root;
};
[leetcode] 104. 二叉树的最大深度
leetcode题目链接
此题比较简单,只需要在遍历的过程中不断记录height 即可,当层序遍历结束,返回height 就解决了。
var maxDepth = function(root) {
if(root === null) return root;
var queue = [root];
var height = 0;
while(queue.length > 0) {
const length = queue.length;
height++;
for(var i = 0; i < length; i++) {
var node = queue.shift();
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
return height
};
[leetcode] 111. 二叉树的最小深度
leetcode题目链接
此题与104. 二叉树的最大深度 类似,区别在于需要提前结束循环,通过判断树节点是否满足node.left === null && node.right === null ,就可以知道二叉树的最小深度是哪个节点,将该节点遍历时的height 返回即可。
代码:
var minDepth = function(root) {
if(root === null) return root;
var queue = [root];
var height = 0;
while(queue.length > 0) {
const n = queue.length;
height++;
for(var i = 0; i < n; i++) {
var node = queue.shift();
if(node.left === null && node.right === null ) {
return height;
}
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
return height;
};
结尾
二叉树的层序遍历本质上是队列 +广度优先遍历 的结合运用诞生出来的"神器",此时就会发现通过它可以解决leetcode 中很多二叉树的题目!
如果本文对你有帮助,不妨点个赞吧~
|