1. 题目解析与核心思路
LeetCode 1161题要求我们找到二叉树中元素和最大的那一层,如果有多个层的和相同则返回层号最小的那个。这道题被标记为中等难度,主要考察对二叉树的层序遍历(BFS)和简单数学运算的应用能力。
1.1 题目关键点拆解
首先我们需要明确几个关键要求:
- 计算每一层所有节点的值之和
- 比较这些和的大小,找出最大值
- 如果存在多个层的和相同,返回层号最小的那个
- 层号从1开始计数(根节点为第1层)
1.2 解题思路选择
对于这类层级相关的问题,广度优先搜索(BFS)是最自然的选择。BFS可以让我们逐层访问节点,正好符合题目中"按层计算"的需求。相比之下,深度优先搜索(DFS)虽然也能实现,但需要额外记录层级信息,代码会稍显复杂。
提示:在二叉树问题中,当需要处理层级关系时,优先考虑BFS;当需要处理路径或深度关系时,优先考虑DFS。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. BFS算法实现详解
2.1 标准BFS模板
标准的BFS实现通常使用队列数据结构,遵循以下步骤:
- 将根节点放入队列
- 当队列不为空时:
a. 记录当前队列大小(即当前层的节点数)
b. 处理当前层的所有节点(出队、计算、子节点入队) - 重复直到队列为空
2.2 本题的BFS适配
针对本题,我们需要在标准BFS基础上增加:
- 层号计数器
- 当前层和计算器
- 最大和及其对应层号的记录
python复制from collections import deque
class Solution:
def maxLevelSum(self, root: TreeNode) -> int:
if not root:
return 0
queue = deque([root])
max_sum = float('-inf')
result_level = 1
current_level = 0
while queue:
current_level += 1
level_size = len(queue)
level_sum = 0
for _ in range(level_size):
node = queue.popleft()
level_sum += node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if level_sum > max_sum:
max_sum = level_sum
result_level = current_level
return result_level
2.3 时间复杂度分析
这个算法的时间复杂度是O(N),其中N是树中的节点数,因为我们需要访问每个节点恰好一次。空间复杂度在最坏情况下也是O(N),即当树是完全不平衡时(每个节点只有一个子节点),队列中最多会存储N个节点。
3. 优化与边界情况处理
3.1 空树处理
虽然题目中给出的树至少有一个节点(根据约束条件),但良好的编程习惯应该包含对空树的处理:
python复制if not root:
return 0 # 或者根据题目要求返回其他值
3.2 大数求和问题
虽然LeetCode的测试用例通常不会出现极端情况,但在实际工程中,当层级节点很多且节点值很大时,求和可能会导致整数溢出。Python本身对整数大小没有限制,但在其他语言如Java、C++中需要考虑这个问题。
3.3 层级编号处理
注意题目要求层号从1开始计数,而不是程序员习惯的0开始。这个小细节在实际编程竞赛中经常导致错误。
4. 替代解法:DFS实现
虽然BFS是更自然的选择,但为了全面理解,我们也可以看看DFS的实现方式:
python复制class Solution:
def maxLevelSum(self, root: TreeNode) -> int:
level_sums = []
def dfs(node, level):
if not node:
return
if len(level_sums) == level:
level_sums.append(0)
level_sums[level] += node.val
dfs(node.left, level + 1)
dfs(node.right, level + 1)
dfs(root, 0)
return level_sums.index(max(level_sums)) + 1
4.1 DFS与BFS比较
- 空间复杂度:DFS在最坏情况下(倾斜树)需要O(N)的栈空间,而BFS需要O(N)的队列空间
- 实现复杂度:DFS需要维护一个额外的数组来存储各层和
- 适用场景:如果树的深度很大但宽度不大,DFS可能更节省内存
5. 常见错误与调试技巧
5.1 典型错误案例
- 忘记处理空子树:
python复制# 错误示例
queue.append(node.left) # 应该先检查node.left是否为空
queue.append(node.right)
- 层号计数错误:
python复制# 错误示例
result_level = 0 # 应该从1开始
- 最大和初始化错误:
python复制# 错误示例
max_sum = 0 # 如果所有节点值为负,会得到错误结果
5.2 调试技巧
- 打印层级信息:
python复制print(f"Level {current_level}: sum = {level_sum}")
- 可视化小树:
code复制 1
/ \
7 0
/ \
7 -8
- 边界测试用例:
- 单节点树
- 完全倾斜的树
- 所有节点值相同的树
- 正负值交替的树
6. 语言特定实现要点
6.1 Python实现技巧
- 使用
collections.deque而非list实现队列,因为它的popleft()操作是O(1)时间复杂度 - 利用
float('-inf')初始化最大和,可以正确处理所有节点为负值的情况 - Python的列表索引从0开始,注意与题目要求的层号转换
6.2 Java实现注意
java复制// Java队列使用示例
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
TreeNode node = queue.poll();
注意处理整数溢出问题,可以使用long类型存储和。
6.3 C++实现要点
cpp复制// C++队列使用示例
queue<TreeNode*> q;
q.push(root);
TreeNode* node = q.front();
q.pop();
注意指针操作和内存管理。
7. 复杂度优化思考
虽然O(N)的时间复杂度已经是最优解,但我们还可以考虑一些微优化:
- 提前终止:如果在某层计算过程中,当前和已经超过剩余所有层可能的最大和,可以提前终止
- 并行计算:对于非常大的树,可以考虑并行处理不同子树(实际LeetCode场景不适用)
- 内存优化:对于DFS实现,可以只记录当前最大和及其层号,而不是存储所有层的和
8. 相似题目扩展
掌握这道题后,可以尝试解决以下相似题目:
- LeetCode 102. 二叉树的层序遍历
- LeetCode 107. 二叉树的层次遍历 II
- LeetCode 103. 二叉树的锯齿形层次遍历
- LeetCode 199. 二叉树的右视图
- LeetCode 515. 在每个树行中找最大值
这些题目都基于BFS对二叉树进行层序遍历,只是处理每层节点的方式不同。
9. 实际应用场景
虽然这道题看起来是纯粹的算法练习,但类似的层级求和思想在实际开发中有广泛应用:
- 组织架构树中统计各部门的绩效总和
- 游戏中的技能树系统计算各层级的技能点消耗
- 电商系统中多级分类的商品数量统计
- 文件系统中目录大小的层级计算
10. 总结与个人心得
这道题很好地展示了BFS在层级遍历中的应用。在实际编程中,我有以下几点体会:
- 模板化思维很重要:掌握BFS的标准模板后,可以快速适配各种变种题目
- 边界条件不能忽视:空树、单节点、全负值等情况经常是测试用例的重点
- 变量命名要清晰:像
current_level、level_sum这样的名称比简单的i、s更易于维护 - 调试时从小树开始:先手动验证小树的正确性,再测试大树的性能
最后,对于二叉树问题,我建议多练习各种遍历方式,理解它们的特点和适用场景。这道题虽然标记为中等,但只要掌握了BFS的核心思想,其实相当直接明了。
