1. 题目解析与解题思路
这道题目要求我们计算二叉树中最深层叶子节点的和。乍一看似乎很简单,但实际处理时需要同时考虑树的深度遍历和特定层级的节点统计。作为刚接触树结构的新手,我最初被这个看似简单的问题卡住了好几个小时。
1.1 问题核心理解
题目给出的二叉树结构定义如下:
struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode() : val(0), left(nullptr), right(nullptr) {} TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} };关键点在于:
- 需要先确定树的最大深度
- 然后收集所有位于该深度的叶子节点
- 最后将这些节点的值相加
1.2 解题思路选择
我最终选择了DFS(深度优先搜索)的递归解法,原因如下:
- 递归天然适合处理树结构
- DFS可以自然地跟踪当前节点的深度
- 相比BFS(广度优先搜索)需要维护队列,DFS实现更简洁
2. 代码实现详解
2.1 最大深度计算
int maxDepth; // 定义最大深度 void getMaxDepth(TreeNode* root, int depth) { if(root == NULL) { // 递归终止条件 return; } if(maxDepth < depth) { maxDepth = depth; // 更新最大深度 } getMaxDepth(root->left, depth+1); // 递归左子树 getMaxDepth(root->right, depth+1); // 递归右子树 }这段代码有几个关键点需要注意:
maxDepth是类成员变量,用于在递归过程中保持状态- 每次递归调用时,当前深度
depth会+1 - 遇到空节点时直接返回,这是递归的终止条件
2.2 最深层节点求和
int sumMaxDepth(TreeNode* root, int depth) { if(root == NULL) { return 0; } if(maxDepth == depth) { return root->val; // 找到目标节点,返回其值 } return sumMaxDepth(root->left, depth+1) + sumMaxDepth(root->right, depth+1); }这个函数的逻辑:
- 同样以空节点作为递归终止条件
- 当当前深度等于最大深度时,返回该节点的值
- 否则继续递归左右子树,并将结果相加
2.3 主函数整合
int deepestLeavesSum(TreeNode* root) { maxDepth = 0; getMaxDepth(root, 0); // 先计算最大深度 return sumMaxDepth(root, 0); // 再求和 }主函数的执行顺序很重要:
- 必须先计算最大深度
- 然后才能基于这个深度求节点和
3. 关键知识点解析
3.1 递归在树结构中的应用
树是递归定义的天然结构,每个子树本身也是一棵树。这种自相似性使得递归成为处理树问题的利器。在本解法中,我们利用递归实现了:
- 深度优先遍历
- 深度信息的传递
- 节点值的累加
3.2 递归函数的返回值处理
这里有一个很重要的细节:
getMaxDepth是void类型,通过修改成员变量maxDepth来传递结果sumMaxDepth是int类型,通过返回值传递计算结果
这种差异反映了递归函数设计的两种常见模式:
- 通过参数或成员变量"向下"传递信息
- 通过返回值"向上"传递计算结果
4. 常见问题与优化思考
4.1 空树处理
当前代码已经考虑了空树的情况:
getMaxDepth遇到空节点直接返回,maxDepth保持初始值0sumMaxDepth遇到空节点返回0,最终和为0
4.2 递归深度限制
对于极端不平衡的树(如退化成链表),递归可能导致栈溢出。这时可以考虑:
- 使用迭代代替递归
- 改用BFS实现
- 增加递归深度限制检查
4.3 时间复杂度分析
该算法的时间复杂度是O(n),其中n是节点数量,因为:
- 计算最大深度需要遍历所有节点
- 求和过程也需要遍历所有节点
- 每个节点只被访问两次
空间复杂度取决于树的高度,最坏情况下是O(n)。
5. 代码优化建议
5.1 合并两次遍历
当前解法遍历了两次树,可以优化为一次遍历:
class Solution { public: int deepest = 0; int sum = 0; void dfs(TreeNode* node, int depth) { if(!node) return; if(depth > deepest) { deepest = depth; sum = node->val; } else if(depth == deepest) { sum += node->val; } dfs(node->left, depth+1); dfs(node->right, depth+1); } int deepestLeavesSum(TreeNode* root) { dfs(root, 0); return sum; } };这个优化版本:
- 只遍历一次树
- 动态更新最大深度和对应节点和
- 减少了重复计算
5.2 迭代实现方案
对于不喜欢递归的开发者,可以用迭代实现:
int deepestLeavesSum(TreeNode* root) { if(!root) return 0; queue<TreeNode*> q; q.push(root); int sum = 0; while(!q.empty()) { int size = q.size(); sum = 0; // 重置当前层级的和 for(int i = 0; i < size; i++) { TreeNode* node = q.front(); q.pop(); sum += node->val; if(node->left) q.push(node->left); if(node->right) q.push(node->right); } } return sum; }这个BFS实现:
- 按层级遍历树
- 最后一层自然就是最深层
- 不需要预先计算深度
6. 学习心得与总结
通过这道题目,我深刻理解了递归在树结构中的应用。几个关键收获:
- 递归终止条件必须明确,否则会导致无限递归
- 递归函数的返回值类型决定了如何处理子问题的结果
- 树的问题通常有多种解法(DFS/BFS,递归/迭代),各有优缺点
- 先理清思路再写代码,比直接动手调试效率高得多
对于树结构的练习,我的建议是:
- 先掌握基本的遍历方式(前序、中序、后序)
- 理解递归的工作原理
- 多做练习题,从简单到复杂逐步提升
这道题目虽然让我纠结了很久,但通过不断调试和思考,最终不仅解决了问题,还对树结构和递归有了更深的理解。这种通过实际问题驱动学习的方式,效果比单纯看书要好得多。