437. 路径总和 III ●●
描述
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。
题解
1. 分治 + 深度优先搜索 DFS
对每一个节点都求一次以该节点为路径起点的路径数量。
节点数值有正有负,所以递归到叶子节点结束。
- 时间复杂度: O ( N 2 ) O(N^2) O(N2),其中 N 为该二叉树节点的个数。对于每一个节点,求以该节点为起点的路径数目时,则需要遍历以该节点为根节点的子树的所有节点,因此求该路径所花费的最大时间为 O(N),我们会对每个节点都求一次以该节点为起点的路径数目,因此时间复杂度为 O ( N 2 ) O(N^{2}) O(N2)。
- 空间复杂度:O(N),考虑到递归需要在栈上开辟空间。
class Solution {
public:
int rootSum(TreeNode* node, long long target){
if(!node) return 0;
int count = 0;
if(target == node->val) ++count; // 当前节点满足条件,计数+1,并继续往下查找
count += rootSum(node->left, target-node->val) + rootSum(node->right, target-node->val);
return count;
// return (target == node->val)
// + rootSum(node->left, target-node->val)
// + rootSum(node->right, target-node->val);
}
int pathSum(TreeNode* root, int targetSum) {
if(!root) return 0;
int ans = 0; // 分治
ans += rootSum(root, targetSum); // 以root为起点,查找以该节点为路径开头,且和为targetSum的路径数量
ans += pathSum(root->left, targetSum); // 以root->left为根的子树中,和为targetSum的路径数量
ans += pathSum(root->right, targetSum); // 以root->right为根的子树中,查找和为targetSum的路径数量
return ans;
}
};
2. 前缀和
解法一中应该存在许多重复计算。
我们定义节点的前缀和为:由根结点到当前结点的路径上所有节点的和。
我们利用先序遍历二叉树,记录下根节点 root 到当前节点 node 的路径上除当前节点以外所有节点的前缀和,在已保存的路径前缀和中查找是否存在前缀和刚好等于当前节点到根节点的前缀和 curr 减去 targetSum,如果存在,表示存在中间节点到 node 节点(结尾)的路径和为 targetSum,且这些路径的数量由 preCnt 前缀和哈希表给出。
注意:
- 对于空路径我们也需要保存预先处理一下,此时因为空路径不经过任何节点,因此它的前缀和为 0,即
preCnt[0] = 1;
- 在退出当前节点的路径时,需要注意更新哈希表中对应前缀和的数量。
- 利用深度优先搜索,计算以每个节点为结尾时的符合条件的路径之和。
- 时间复杂度:O(N),其中 N 为二叉树中节点的个数。利用前缀和只需遍历一次二叉树即可。
- 空间复杂度:O(N)。
class Solution {
public:
unordered_map<long long, int> preCnt; // <路径和, 个数>
int dfs(TreeNode* node, long long curr, int target){
if(node == nullptr) return 0;
int cnt = 0;
curr += node->val; // curr 为根节点到 node 节点的路径和,存在curr-target表示存在中间节点到node节点的路径和为target
if(preCnt.count(curr-target)){
// 以node节点结尾的路径和为target的数量
cnt += preCnt[curr-target];
}
++preCnt[curr]; // 哈希记录路径和
cnt += dfs(node->left, curr, target); // 以node->left结尾的路径和为target的数量
cnt += dfs(node->right, curr, target); // 以node->eight结尾的路径和为target的数量
--preCnt[curr]; // 退出当前节点,回溯
return cnt;
}
int pathSum(TreeNode* root, int targetSum) {
preCnt[0] = 1; // 有一条空路径,和为0
return dfs(root, 0, targetSum);
}
};