必须边递归边维护当前路径并回溯:进入子节点前push,返回前pop;仅传sum无法还原路径;path和result须传引用;叶子节点且remain==node->val时收集路径;空节点直接返回。

如何用递归回溯求二叉树中所有从根到叶子的路径和等于目标值的路径
直接说结论:必须边递归边维护当前路径(vector<int> 或栈),到达叶子时判断累加和是否等于 targetSum;不能只传一个 sum 变量完事,否则无法还原路径。回溯的关键在于「进入子节点前 push,返回父节点前 pop」。
常见错误现象:result 里全是空 vector,或所有路径都重复、长度不对;根本原因是没在递归返回时正确 pop_back(),或者误把 path 按值传递导致修改不回传。
- 使用场景:LeetCode 113 题「路径总和 II」,要求返回所有满足条件的路径(不是只返回 bool)
- 参数差异:函数签名建议为
void dfs(TreeNode* node, int remain, vector<int>& path, vector<vector<int>>& result),其中remain是剩余需凑齐的值(避免反复计算 sum),path和result必须传引用 - 性能影响:每条路径拷贝一次进
result,时间复杂度 O(N × H),H 是树高(最坏路径长度),空间复杂度 O(H)(递归栈 + 当前路径)
为什么不能只靠全局变量或 static path 来存路径
多个测试用例连续运行时,static 变量不清空会导致上一次的 path 残留;而全局变量在多线程或重入场景下彻底不可控。更隐蔽的问题是:递归过程中不同分支共用同一份 path,若未严格配对 push/pop,某次 pop 可能删掉其他分支还没处理完的数据。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 始终把
path作为非 const 引参传入,不声明 global/static - 每次调用
dfs前检查node != nullptr,空节点直接 return,避免野指针 - 叶子节点判断必须是
!node->left && !node->right,仅判!node不够
递归终止条件与路径收集时机怎么写才不出错
终止条件只有两个:空节点(直接返回)、叶子节点且 remain == node->val(此时收集路径)。注意:不能在进入函数时就检查 remain == 0,因为可能还没走到叶子;也不能等 remain < 0 就剪枝——题目没说节点值全为正,负数节点合法。
示例关键片段:
void dfs(TreeNode* node, int remain, vector<int>& path, vector<vector<int>>& result) {
if (!node) return;
path.push_back(node->val);
if (!node->left && !node->right && remain == node->val) {
result.push_back(path); // 此处拷贝当前完整路径
}
dfs(node->left, remain - node->val, path, result);
dfs(node->right, remain - node->val, path, result);
path.pop_back(); // 回溯:必须放最后,且与 push_back 成对
}
调用入口和边界 case 容易漏哪些点
入口函数常忘判根为空:if (!root) return result;。另外,如果题目允许空路径(sum=0 且树为空),要单独处理;但常规题中 root 为空时直接返回空 result 即可。
容易被忽略的细节:
-
remain - node->val在左右子树递归中必须一致,别写成remain或remain - node->val * 2 - C++ 中
vector<int>拷贝是深拷贝,result.push_back(path)安全,不用手动 new - 若用
int sum累加代替remain,需在叶子处判断sum == targetSum,逻辑等价但不如 remain 直观
路径数组的生命周期和拷贝时机,比想象中更关键;少一次 pop_back,整个结果就全乱了。


















