题目
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
思路
方法一:双重递归
思想:首先先序递归遍历每个节点,再以每个节点作为起始点递归寻找满足条件的路径。
java代码如下:
class Solution {
int pathnumber;
public int pathSum(TreeNode root, long sum) {//因为力扣有个特别变态的测试用例,[1000000000,1000000000,null,294967296,null,1000000000,null,1000000000,null,1000000000],int会溢出报错,所以要用long
if(root == null) return 0;
Sum(root,sum);
pathSum(root.left,sum);
pathSum(root.right,sum);
return pathnumber;
}
public void Sum(TreeNode root, long sum){
if(root == null) return;
sum -= root.val;
if(sum == 0){
pathnumber++;
}
Sum(root.left,sum);
Sum(root.right,sum);
}
}
方法二:深度优先搜索
访问每一个节点 node
,检测node
为起始节点且向下延深的路径有多少种,递归遍历每一个节点的所有可能的路径,然后将这些路径数目加起来即为返回结果。
- 定义
rootSum(p,val)
表示以节点p
为起点向下且满足路径总和为val
的路径数目,对二叉树上每个节点p
求出rootSum(p,targetSum)
,然后对这些路径数目求和即为返回结果。 - 对节点
p
求rootSum(p,targetSum)
时,以当前节点p
为目标路径起点,不断递归向下进行搜索。假设当前的节点p
的值为val
,对左子树和右子树进行递归搜索,对节点p
的左孩子节点p_left
求出rootSum(p_left,targetSum−val)
,以及对右孩子节点p_right
求出rootSum(p_right,targetSum−val)
。节点p
的rootSum(p,targetSum)
即等于rootSum(p_left,targetSum−val)
与rootSum(p_right,targetSum−val)
之和,同时还需要判断一下当前节点p
的值是否刚好等于targetSum
- 采用递归遍历二叉树的每个节点
p
,对节点p
求rootSum(p,val)
,然后将每个节点所有求的值进行相加求和返回。
java代码如下:
class Solution {
//同理参数类型也是用long,否则有一个变态的测试用例通不过
public int pathSum(TreeNode root, long targetSum) {//pathSum表示最终的结果,包含每一个节点的结果
if (root == null) {
return 0;
}
int ret = rootSum(root, targetSum);
ret += pathSum(root.left, targetSum);
ret += pathSum(root.right, targetSum);
return ret;
}
public int rootSum(TreeNode root, long targetSum) {//rootSum表示当前节点开始往下的路径数目
int ret = 0;
if (root == null) {
return 0;
}
int val = root.val;
if (val == targetSum) {
ret++;
}
ret += rootSum(root.left, targetSum - val);
ret += rootSum(root.right, targetSum - val);
return ret;
}
}
方法三:前缀和
对方法二中的重复计算进行优化
java代码如下:
class Solution {
public int pathSum(TreeNode root, int targetSum) {
Map<Long, Integer> prefix = new HashMap<Long, Integer>();
prefix.put(0L, 1);//当targetSum 等于某个节点值时,curPrefix - targetSum = 0,即当前节点前缀和为0,但是当前节点自己也算做一条符合条件的路径,所以也要计数
return dfs(root, prefix, 0, targetSum);
}
public int dfs(TreeNode root, Map<Long, Integer> prefix, long curr, int targetSum) {
//递归终止的条件
if (root == null) {
return 0;
}
//先序遍历
int ret = 0;
curr += root.val;//当前节点的前缀和
ret = prefix.getOrDefault(curr - targetSum, 0);//当前节点的前缀和,如果前缀和中存在curr - targetSum,则返回对应的value,否则返回0
prefix.put(curr, prefix.getOrDefault(curr, 0) + 1);
ret += dfs(root.left, prefix, curr, targetSum);
ret += dfs(root.right, prefix, curr, targetSum);
//因为先序遍历是遍历根、左、右,即当前节点及其所有子节点,所以当遍历完当前节点和其所有子节点之后,当前节点的前缀和就没有用了,就需要把map里的记录删除,否则会影响其他子树的计算。跟当前节点没有路径关系的节点,不需要当前节点的前缀和
prefix.put(curr, prefix.getOrDefault(curr, 0) - 1);
return ret;
}
}