之前在 5337.每个元音包含偶数次的最长子字符串——状态压缩DP一文中介绍过状态压缩DP,里面有个思想其实也是利用了前缀和,和本文很相似。
引入
本题🔗如下:
437.路径总和 III
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
本题,一开始是想到的是下面的解法,
class Solution {
int pathnumber;
public int pathSum(TreeNode root, int sum) {
if(root == null) return 0;
Sum(root,sum);
pathSum(root.left,sum);
pathSum(root.right,sum);
return pathnumber;
}
public void Sum(TreeNode root, int sum){
if(root == null) return;
sum-=root.val;
if(sum == 0){
pathnumber++;
}
Sum(root.left,sum);
Sum(root.right,sum);
}
}
很明显,这样解法很复杂,因为在每个节点上都要重新开始一次dfs。
所以,这里我们想到使用前缀和来解。
前缀和题解
如果两个数的前缀总和是相同的,那么这些节点之间的元素总和为零。进一步扩展相同的想法,如果前缀总和currSum,在节点A和节点B处相差target,则位于节点A和节点B之间的元素之和是target。
所以,我们需要用一个Map来记录一条路径上的前缀和,再让目前的值currSum来减去target,如果Map中记录了(currSum-target)的键值对(currSum-target,value)
,那么表示找到了value条满足要求的路径。
废话不多说,直接上代码:
class Solution {
Map<Integer,Integer> map;
int target;
public int pathSum(TreeNode root, int sum) {
this.target=sum;
this.map=new HashMap<>();
//初始化:前缀和为0的肯定会有一条,即节点本身
map.put(0,1);
return helper(root,0);
}
public int helper(TreeNode node,int currSum){
if(node==null){
return 0;
}
currSum+=node.val;
int count=0;
count+=map.getOrDefault(currSum-target,0);
//将本节点的前缀和加入map
map.put(currSum,map.getOrDefault(currSum,0)+1);
//左、右子树满足的路径数目
count+=helper(node.left,currSum);
count+=helper(node.right,currSum);
//回溯,去除当前节点的前缀和
map.put(currSum,map.get(currSum)-1);
return count;
}
}