LeetCode 面试题 04.12. 求和路径
题目描述

题意分析
统计节点值之和等于目标的非空路径数量。路径可以从任意节点开始,也可以在任意节点结束,但只能沿父到子的方向连续向下,不能从一个分支绕到另一个分支。
解法:祖先前缀和计数并回溯
核心思路
[!blue]
固定一个路径终点后,它的起点只可能是自己或某个祖先。若对每个终点重新枚举全部祖先会重复求和,可以把当前根到节点的路径看作一个数组,使用前缀和之差计算中间一段。
设从树根到当前节点的累计和为
s,某个候选起点的父节点处前缀和为p,那么这段向下路径的和就是s - p。要让它等于target,只需找到前缀和为s - target的祖先位置。用cnt保存当前祖先路径中各前缀和的出现次数,一次查询就能得到以当前节点结尾的合法路径数量。频次不能只记是否出现,因为不同祖先可能有相同前缀和,对应不同起点。开始登记一次空前缀
0,把根之前的虚拟位置也计入,才能统一统计从根开始的路径。到达当前节点时,先把节点值加入
s,查询cnt[s - target],然后才登记当前的s。顺序不能交换,否则目标为 0 时会把当前前缀与自身配对,误计一条没有节点的空路径。登记当前前缀后,左右孩子才能把它当作各自路径起点之前的位置。左右子树处理完后,将当前
s的次数减一,再返回父节点。这样计数表中有效的前缀始终只来自当前祖先链,兄弟分支不会互相拼接。每条合法路径恰好在它的终点被统计一次,把当前贡献与左右子树答案相加即可。
解题步骤
- 先登记空前缀 0 一次。
- 累加当前节点,先查询 s-target,再登记当前 s。
- 递归左右孩子并累加结果。
- 返回父节点前撤销当前前缀的一次计数。
空节点没有以它为终点的路径,返回 0。节点值可以为负数或 0,当前前缀和超过目标也不能提前停止;后面的节点仍可能把路径和拉回目标。
代码实现
class Solution {
private Map<Long, Integer> cnt = new HashMap<>();
private int target;
public int pathSum(TreeNode root, int sum) {
// 空前缀,用于统计从根开始的路径。
cnt.put(0L, 1);
target = sum;
return dfs(root, 0);
}
private int dfs(TreeNode root, long s) {
if (root == null) {
return 0;
}
s += root.val;
// 必须先查后写,否则 target 为 0 时会与自身配对。
int answer = cnt.getOrDefault(s - target, 0);
cnt.merge(s, 1, Integer::sum);
answer += dfs(root.left, s);
answer += dfs(root.right, s);
// 回溯:让兄弟分支看不到本条链上的前缀和。
cnt.merge(s, -1, Integer::sum);
return answer;
}
}
func pathSum(root *TreeNode, sum int) int {
// 空前缀,用于统计从根开始的路径。
cnt := map[int64]int{0: 1}
var dfs func(*TreeNode, int64) int
dfs = func(root *TreeNode, s int64) int {
if root == nil {
return 0
}
s += int64(root.Val)
// 必须先查后写,否则 sum 为 0 时会与自身配对。
answer := cnt[s-int64(sum)]
cnt[s]++
answer += dfs(root.Left, s)
answer += dfs(root.Right, s)
// 回溯:让兄弟分支看不到本条链上的前缀和。
cnt[s]--
return answer
}
return dfs(root, 0)
}
复杂度分析
- 时间复杂度:期望 $O(n)$,每个节点只做常数次哈希查询和计数更新。
- 空间复杂度:总额外空间 $O(n)$。递归栈为 $O(h)$;代码没有删除计数降为 0 的键,映射最多保留 $O(n)$ 个曾出现的前缀。
关键点总结
[!green]
查询的是起点之前的祖先前缀,登记供后代使用,回溯时撤销以隔离兄弟分支。Java
long与 Goint64保存累计和,避免多节点累加时溢出。
易错点总结
[!yellow]
- 必须先查询再登记,否则 target=0 时会把空路径计入。
- 离开节点时不撤销会让兄弟分支互相配对。
- 负数和零都允许,不能按当前和大小剪枝。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 560. 和为 K 的子数组 | 中等 | 把数组前缀和计数推广到树路径,新增的是退出分支时的撤销。 |
| 113. 路径总和 II | 中等 | 原题只找根到叶路径并输出方案,本题允许任意祖先起点且只计数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!