题目描述

✅ 1161. 最大层内元素和

image-20260928233927229

image-20260928233927230

题意分析

对二叉树每一层的全部节点值求和,找到层和最大的那一层,并返回它的层号。根是第一层;如果多个层的和同为最大值,返回其中最小的层号。

比较的是整层总和,不是层内最大节点值或最多节点数。节点值可以为负,所以最大层和也可能为负;题目保证树非空,并且节点数与值范围允许层和使用 32 位整数。

解法:BFS 分层求和并保留最早最大值

核心思路

[!blue]

答案按层聚合,可以用 BFS。每轮开始时,队列中尚未处理的节点恰好属于同一层,先记住这一层的节点数,只弹出这么多个节点,便能独立计算当前层的和。

处理节点时,它的非空孩子追加到队尾,属于下一层。固定本轮数量后,这些新入队节点不会提前混入当前层;本轮结束时,待处理部分也就自然变成下一层,层号可以增加一次。

历史最大和初始化为比任意合法层和都小的整数,而不是零,才能让全负树中的真实层参与比较。只有当前层和严格大于历史最大值才更新答案;BFS 按层号递增处理,相等时保留旧答案,就自动保留了并列中的最小层号。

Java 队列会弹出已经处理的节点;Go 代码用 head 在一个不断追加的切片中前移,所以本层大小是 len(queue) - head,不能使用整个切片长度。两种写法的待处理边界含义一致,但 Go 仍保留已处理元素,空间分析要分别说明。

解题步骤

  1. 将根节点放入队列,层号从零起步,最大和置为最小整数,答案层号初始为一。
  2. 每轮固定尚未处理节点数,层号加一,并把本层和重置为零。
  3. 处理恰好这一层的节点,累加节点值,把非空孩子加入队尾。
  4. 当前层和严格更大时,更新最大和与层号;相等时保持旧答案。
  5. 全部节点处理完后返回答案层号。

代码实现

class Solution {
    public int maxLevelSum(TreeNode root) {
        Queue<TreeNode> queue = new ArrayDeque<>();

        // 根节点单独构成第 1 层
        queue.offer(root);

        // 和最大的层号,兜底取合法下界 1
        int ans = 1;
        // 初值要比任何层和都小,不能写 0
        int maxSum = Integer.MIN_VALUE;
        // 当前正在处理的层号
        int level = 0;

        while (!queue.isEmpty()) {
            // 先快照:此刻队列里恰好是本层全部节点
            int size = queue.size();

            level++;
            // 本层元素和
            int sum = 0;

            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();

                sum += node.val;

                if (node.left != null) {
                    // 下一层节点追加到队尾
                    queue.offer(node.left);
                }

                if (node.right != null) {
                    queue.offer(node.right);
                }
            }

            // 严格大于:并列时保留先出现的较小层号
            if (sum > maxSum) {
                maxSum = sum;
                ans = level;
            }
        }

        return ans;
    }
}
func maxLevelSum(root *TreeNode) int {
    // 根节点单独构成第 1 层
    queue := []*TreeNode{
        root,
    }

    // 和最大的层号,兜底取合法下界 1
    ans := 1
    // 初值要比任何层和都小,不能写 0
    maxSum := -1 << 31
    // 当前正在处理的层号
    level := 0

    for head := 0; head < len(queue); {
        // 先快照:未处理的部分恰好是本层全部节点
        size := len(queue) - head
        level++
        // 本层元素和
        sum := 0

        for i := 0; i < size; i++ {
            node := queue[head]
            head++
            sum += node.Val

            if node.Left != nil {
                // 下一层节点追加到队尾
                queue = append(queue, node.Left)
            }
            if node.Right != nil {
                queue = append(queue, node.Right)
            }
        }

        // 严格大于:并列时保留先出现的较小层号
        if sum > maxSum {
            maxSum = sum
            ans = level
        }
    }

    return ans
}

复杂度分析

  • 时间复杂度:$O(n)$,每个节点入队、处理一次,每层只做一次总和比较。
  • 空间复杂度:Java 队列为 $O(w)$,w 为最大层宽,处理途中至多同时保存相邻两层的部分节点;Go 切片保留全部加入过的节点,因此当前实现为 $O(n)$。

关键点总结

[!green]

  • 先固定层大小,再把孩子入队,保证求和范围只属于当前层。
  • 最大和必须允许负值,不能用零作为未找到答案的下界。
  • 按层递增搜索并只在严格更大时更新,自然满足最小层号的并列规则。
  • 返回层号,层和只用于比较,不能混淆两者。

易错点总结

[!yellow]

  • 本层和没有归零,会把不同层的累计值拿来比较。
  • 内层循环动态读取不断增长的队列长度,会把下一层提前并入本层。
  • 使用大于等于更新,会在并列时改成更深的层号。
  • 最大和初始化为零,全负树的任何层都无法正确刷新。
  • Go 用整个切片长度作为本层大小,忽略前面已经处理的元素,会重复处理或越界。

相似题目

题目 难度 关联与区别
102. 二叉树的层序遍历 中等 同样按层锁定队列长度,原题收集节点值,本题对每层求和并记录层号。
515. 在每个树行中找最大值 中等 同样做逐层聚合,原题取每层最大元素,本题比较整层总和。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/14392977
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!