找前缀和,很抽象的一种解法。
如何在面试时快速想出来?
- 看看有没有前缀
currSum - targetSum? - 保存当前
currSum为前缀。 - 前缀数量减一,防止影响右子树。
currSum表示(图中简写curr),当前结点到根节点,从下往上这条路径上的值的和。- 看图一中左边的黄圈,绿色结点
3的curr值是18,表示从当前结点到根结点累积的和是18,如果curr - target = 18 - 8 = 10存在(10存在),那么和为8的curr肯定存在。因为18存在,10存在,那么8肯定存在。 - 对于结点
node,哈希表中的key为node的currSum,value为currSum - target的数量。dfs前value + 1,node递归完成后value - 1,防止node的左子树影响右子树。 ret保存每次递归返回的值。表示在当前结点node的子树中和为target的路径数量,哈希表中存在多少个curr - target,当前子树就存在多少个target。👇
ret = preSum.getOrDefault(currSum - targetSum, 0);
preSum.put(currSum, preSum.getOrDefault(currSum, 0) + 1);
- 为什么要
put <0, 1>?因为currSum - target == 0的时候,也就是key == 0时,也是一个答案。 - 为什么上面两行代码不能交换?是为了防止
target为0的情况。如果交换代码,先更新哈希表,表示找到了一条curr路径,但是target是0,再更新ret时curr - target就不为0了。
class Solution {
HashMap<Long, Integer> preSum = new HashMap<>();
public int pathSum(TreeNode root, int targetSum) {
preSum.put(0L, 1);
return dfs(root, 0, targetSum);
}
public int dfs(TreeNode node, long currSum, int targetSum) {
if (node == null) {
return 0;
}
int ret = 0;
currSum += node.val;
ret = preSum.getOrDefault(currSum - targetSum, 0);
preSum.put(currSum, preSum.getOrDefault(currSum, 0) + 1);
ret += dfs(node.left, currSum, targetSum);
ret += dfs(node.right, currSum, targetSum);
preSum.put(currSum, preSum.getOrDefault(currSum, 0) - 1);
return ret;
}
}