图解LeetCode——1161. 最大层内元素和(难度:中等)

一、题目

给你一个二叉树的根节点 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^)/ ~ 「干货分享,每天更新」

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值