当前位置:网站首页>图解LeetCode——1161. 最大层内元素和(难度:中等)
图解LeetCode——1161. 最大层内元素和(难度:中等)
2022-08-02 00:00:00 【爪哇缪斯】
一、题目
给你一个二叉树的根节点 root
。设根节点位于二叉树的第 1
层,而根节点的子节点位于第 2
层,依此类推。
请返回层内元素之和 最大 的那几层(可能只有一层)的层号,并返回其中 最小 的那个。
二、示例
2.1> 示例 1:
【输入】root = [1,7,0,7,-8,null,null]
【输出】2
【解释】 第 1 层各元素之和为 1, 第 2 层各元素之和为 7 + 0 = 7, 第 3 层各元素之和为 7 + -8 = -1, 所以我们返回第 2 层的层号,它的层内元素之和最大。
2.2> 示例 2:
【输入】root = [989,null,10250,98693,-89388,null,null,null,-32127]
【输出】2
提示:
- 树中的节点数在
[1, 104]
范围内-10^5
<= Node.val <=10^5
三、解题思路
因为题目中的要求是统计每层的总数,并返回最大总和的层级。那么既然涉及到层级,第一反应就是通过广度优先算法来逐层遍历。而如果要实现逐层遍历,我们可以借助队列的特性——先进先出来实现。
但是广度优先算法虽然会逐层的对节点进行遍历,但是,如何才能加入“层级”的概念呢?即,某个节点是第几层的。如果解决了层级的问题,这道题的解题思路自然就完整了。
3.1> 广度优先算法+虚拟头节点
为了解决层级问题,最初的一个想法就是——增加虚拟节点。即,首先将根节点标注为firstNodeOfLevel
节点,那么它的作用就是用来标注某层的第一个节点,也就是某一层的头节点。然后从根节点开始,如果获得它的左子节点有值,那么就将firstNodeOfLevel
赋值为左子节点,即:下一层的头节点。随着广度遍历的进行,当发现从队列中获得的节点等于firstNodeOfLevel节点,也就说明,遍历到了某一层的头节点了,那么再获取头节点的左子树节点,赋值为firstNodeOfLevel,并以此类推。但是,如果某个头节点的左子树并不存在,那么我们创建一个虚拟的头节点,val=0,左右子树都为null。每当发现当前从队列中获取的节点就是firstNodeOfLevel节点的是,我们就对上一层遍历到的节点进行最大值判断,如果是当前最大值,则作为预备结果进行保存。
具体操作如下所示,首先将根节点放入到队列中,此处使用的是ArrayDeque
实例对象。那么根节点Node(1)也就是firstNodeOfLevel节点。如果队列不为空,则进入while循环中,只有当队列为空,则结束while循环。在while循环中,从队列中获取头节点Node(1),由于Node(1)是firstNodeOfLevel节点,所以,获取Node(1)的左子节点Node(7)作为“全新的”firstNodeOfLevel节点,并随之把左右子树节点都放入到队列中,并且要统计该层的总和,由于当前是第一层,所以每层总和最大值暂时就是第1层了。具体如下图所示:
此时的firstNodeOfLevel是第二层的头节点了,依然通过获取其左子树来为firstNodeOfLevel赋新值,并且将Node(7)和Node(0)各自的左右节点都放入到队列中。最后要统计该层的总和,由于当前总数是7,比第一层总数1要大,所以每层总和最大值暂时就是第2层了。具体如下图所示:
当我们遍历到第三层的是,其实是最后一层了,前面我们曾经说过,当获取firstNodeOfLevel的左子节点为null的时候,我们要创建虚拟节点,即:val=0且左右子节点都为null的节点。但是,这里有个特殊的处理,就是,如果发现队列中只有firstNodeOfLevel这一个节点,那么其实说明了firstNodeOfLevel节点所在的这层也只有它自己一个节点了,所以也没必要再创建虚拟节点了,那么就当计算完第3层的总数值之后,直接结束while循环了。其中由于第3层的总数为-1,小于第2层的总数7,所以,最终每层总数最大的层数就是2了。这也就是整个方法的最终结果。具体如下图所示:
具体实现请参照4.1> 广度优先算法+虚拟头节点
3.2> 广度优先算法+层级总数统计
在3.1中,我们发现,其实这个所谓的虚拟节点的作用,就是为了标志某个节点是某层的头节点,从而方便我们去统计这层的总数。但是,这种做法其实比较麻烦,而且会导致代码量很大。其实有一个更好的办法,就是通过队列中的size。从3.1图例中我们可以发现,将某一层里的所有节点都放到Queue队列中,然后通过queue.size()
来确定当前队列中有多少个节点是这个层的,然后通过这些节点去做两件事就可以了:
事件一:获取节点的val值,并进行相加统计。
事件二:将该层中每个节点的leftNode和rightNode都放入到队列中。因为在此之前,我们已经确定了队列中有多少个节点是这层的,所以此时往队列中再放入节点的时候,是不会对该层循环统计造成什么影响的。
由于该方法只是不需要再创建firstNodeOfLevel节点指针了,而其他操作与3.1一致,所以此处就不再通过画图解释了。
那么由于我们只是通过对某层的queue.zise()来确定层级中节点的个数,免去了创建和控制firstNodeOfLevel节点的逻辑代码,所以这个实现无论是代码量还是执行效率都要比3.1高了不少。所以,推荐大家采用这种方法。
具体实现请参照4.2> 广度优先算法+层级总数统计
四、代码实现
4.1> 广度优先算法+虚拟头节点
public int maxLevelSum(TreeNode root) {
if (root == null) return 0;
int level = 1, sumNum = root.val, maxNum = sumNum, result = level;
TreeNode firstNodeOfLevel = root.left != null ? root.left : new TreeNode(0);
Queue<TreeNode> queue = new ArrayDeque();
queue.add(firstNodeOfLevel);
if (root.right != null) queue.add(root.right);
while(!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node == firstNodeOfLevel) {
if (maxNum < sumNum) {
maxNum = sumNum;
result = level;
}
if (node.left != null || node.right != null || !queue.isEmpty()) {
TreeNode leftNode = node.left != null ? node.left : new TreeNode(0);
queue.add(leftNode);
firstNodeOfLevel = leftNode;
sumNum = 0;
level++;
}
} else if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
sumNum += node.val;
}
if (maxNum < sumNum) {
result = level;
}
return result;
}
4.2> 广度优先算法+层级总数统计
public int maxLevelSum(TreeNode root) {
Deque<TreeNode> queue = new ArrayDeque();
int max = Integer.MIN_VALUE, level = 1, maxSum = max, result = level;
queue.add(root);
while (!queue.isEmpty()) {
int queueSize = queue.size(), levelSum = 0;
while (queueSize > 0) {
TreeNode node = queue.poll();
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
levelSum += node.val;
queueSize--;
}
if (maxSum < levelSum) {
maxSum = levelSum;
result = level;
}
level++;
}
return result;
}
今天的文章内容就这些了:
写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。
更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」
边栏推荐
猜你喜欢
随机推荐
QML包管理
如何设计循环队列?快进来学习~
ICLR 2022 Best Paper: Partial Label Learning Based on Contrastive Disambiguation
oozie startup error on cdh's hue, Cannot allocate containers as requested resource is greater than maximum allowed
security cross-domain configuration
LeetCode_518_零钱兑换Ⅱ
An interview question about iota in golang
使用Jenkins做持续集成,这个知识点必须要掌握
Use Jenkins for continuous integration, this knowledge point must be mastered
Artifact XXXwar exploded Artifact is being deployed, please wait...(已解决)
解析正则表达式的底层实现原理
【无标题】
1个月写900多条用例,二线城市年薪33W+的测试经理能有多卷?
一个有些意思的项目--文件夹对比工具(一)
ES中SQL查询详解
With a monthly salary of 12K, the butterfly changed to a new one and moved forward bravely - she doubled her monthly salary through the career change test~
分享一份接口测试项目(非常值得练手)
如何进行数据库备份
@Transactional注解在类上还是接口上使用,哪种方式更好?
GetHashCode方法与=