掘金团队号上线,助你 Offer 临门! 点击 查看详情
二叉树的层序遍历(题号102)
题目
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
示例:
二叉树:[3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回其层序遍历结果:
[
[3],
[9,20],
[15,7]
]
链接
解释
这题其实感觉比较简单,就是一层层的循环跟节点,逻辑上没有什么需要注意的,就是代码的书写上有一些需要注意的问题,看看👇的代码就知道了。
自己的答案
var levelOrder = function(root) {
if (!root) return []
var arr1 = [root]
arr2 = []
while (arr1.length > 0) {
const { val, root } = arr1.reduce((total, item) => {
total.val.push(item.val)
item.left && total.root.push(item.left)
item.right && total.root.push(item.right)
return total
}, { val: [], root: [] })
arr1 = root
arr2.push(val)
}
return arr2
};
arr1
用来存储节点,arr2
用来存储最后的值。
首选处理root
不存在的情况,这一点毋庸置疑。
之后开始遍历arr1
,如果它存在,那么就循环它里面的内容,循环时要做两件事,首先拿到当前节点的值,之后拿到它的左节点和右节点,循环完毕后分别放到arr1
和arr2
中即可。
一开始用的是map
,后来想了想,用了reduce
,代码看上去确实更好一些,但感觉没有本质上的改变,依然需要两变量来存储每次循环后拿到的值,此时就有值得提升的地方了。
更好的方法
var levelOrder = function(root) {
if (!root) return []
var arr1 = [root]
arr2 = []
while (arr1.length > 0) {
var arrL = arr1.length
arr2.push([])
for (let i = 0; i < arrL; i++) {
const node = arr1.shift();
arr2[arr2.length - 1].push(node.val)
node.left && arr1.push(node.left)
node.right && arr1.push(node.right)
}
}
return arr2
};
这里相对于我自己的答案,并没有新建两个变量来存储节点和值,它其实利用了while
的特性,我即使在内部修改arr1
,但是在本次while
循环结束前,其内部的arr1
是不会受到影响的,那么就可以放心得对arr1
和arr2
进行处理了。
首先拿到循环前的arr1
的长度,之后一个for
循环,此时可以判断应该从arr1
拿出几个节点,也不会拿太多,拿到下一轮循环的节点。
处理arr2
是就更加简单了,在for
循环开始前,先往arr2
中插入一个空数组,之后在for
循环中直接往arr2
的最后一个元素中插入值即可,也不用新建一个变量来存储for
循环中拿到的val
来。
简单来说就是这样了,别的都没啥,就是细节都处理上不够到位,有待提高。
二叉树的最大深度(题号104)
题目
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
链接
解释
这题主要有两种方法来解答,一种是广度优先算法(Breadth-First-Search),一种是深度优先算法(Depth-First-Search)
自己的答案(广度优先算法)
var maxDepth = function(root) {
if (!root) return 0
var level = 0
nodes = [root]
while (nodes.length > 0) {
var nL = nodes.length
for (let i = 0; i < nL; i++) {
var node = nodes.shift()
node.left && nodes.push(node.left)
node.right && nodes.push(node.right)
if (i === nL - 1) level++
}
}
return level
};
这没啥可说的 ,上一题十分类似,横向遍历所有节点,然后累计遍历层数
自己的答案(深度优先算法)
var maxDepth = function (node, length = 0) {
if (node) {
var left = maxDepth(node.left, length + 1)
right = maxDepth(node.right, length + 1)
return Math.max(left, right) || 1 + length
} else {
return 0
}
};
这方法看上去不错,而且是纯自己想出来的,没有参考别的资料什么的。不过一如既往,还是有些小瑕疵的,比方说这一行:
return Math.max(left, right) || 1 + length
这里感觉是有些奇怪的,本来是这样的:
return Math.max(1, left, right) + length
可是怎么算都不对,之后无意间尝试了上面的一种写法,然后就对了,之后就很莫名其妙,为什么会对呢?原来上面的写法将整体的运算逻辑改变了,如果Math.max
是0,就直接返回1 + length
,这样整体逻辑就对了,
好好想想,如果还是不明白的话。
更好的答案(深度优先算法)
const maxDepth = (root) => {
if (root == null) return 0;
const leftMaxDepth = maxDepth(root.left);
const rightMaxDepth = maxDepth(root.right);
return 1 + Math.max(leftMaxDepth, rightMaxDepth);
};
毕竟是第一次写深度优先算法,看了看别人的,发现自己还是有提升空间的,首先来说可以将root
不存在的情况提出来,后续就是根本不需要第二个length
参数,只需要每次累计1即可,然后进行递归调用,这其实和求平方那一题有点像,完全不要一个length
来进行中转,递归的过程中并不需要这个数。
所以说以后写代码不要着急,仔细想想在动笔,会清晰很多的。
PS:想查看往期文章和题目可以点击下面的链接:
这里是按照日期分类的👇
经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇
有兴趣的也可以看看我的个人主页👇