题目
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。
使用双重递归解决问题
双重递归的主要应用于需要对每个节点进行相似的计算的情景,第一个递归用来遍历这些节点,这二个递归用来对这些节点进行计算,进行深度优先搜索。
首先针对二叉树进行先序遍历,访问每一个节点n。在先序遍历的过程中,以遍历到的每个子树的根节点n作为起始点,将子树看成一张图,进行深度优先搜索,寻找满足条件的路径数量。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res = 0;
int pathSum(TreeNode* root, int targetSum) {
if(root == nullptr){
return 0;
}
cal(root,targetSum); //检查以当前根节点为搜索起始点的图,有多少种满足条件的路径
pathSum(root->left,targetSum); //检查以左孩子为搜索起始点的图,有多少种满足条件的路径
pathSum(root->right,targetSum);//检查以右孩子为搜索起始点的图,有多少种满足条件的路径
return res;
}
void cal(TreeNode* start,int targetSum){ //以start为图的起始点,进行深度优先搜索
if(start == nullptr) return;
if(targetSum - start->val == 0) res ++; //减去本节点的值以后为0,则说明本节点是一条路径的终点。
cal(start->left,targetSum - start->val);
cal(start->right,targetSum - start->val);
}
};
更优的解法:前缀和
定义节点的前缀和为:由根结点到当前结点的路径上所有节点的和。
利用深度搜索对二叉树进行先序遍历,记录下根节点 root 到当前节点 p 的路径上,除当前节点p以外所有节点的前缀和。同时,我们也可以计算出当前节点p的前缀和。使用hash表来存储每一种出现的前缀和对应的节点数。(map<前缀和x,前缀和为x的节点数>)
在遍历过程中,假设我们当前访问到的节点为p,从根节点到当前节点p的前缀和为curr,这时候就从map中查找前面是否存在前缀和刚好等于curr-targetSum。假设从根节点root到节点p的路径上,存在一个节点t,root到t的前缀和为curr-targetSum,则表明发现一条路径t到p,路径和为targetSum。
假设前缀和为curr-targetSum的节点数有res个,则说明t到p路径和为targetSum的节点有res个。res再加上左右子树中满足条件的节点个数,即为最后的答案。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
map<int, int> count;
int pathSum(TreeNode* root, int sum) {
count[0] = 1; //先设定前缀和为0的节点有1个
return helper(root, sum, 0);
}
int helper(TreeNode* root, int sum, int prefix_sum) {
if (!root) return 0;
int res = 0;
prefix_sum += root->val; //计算当前节点的前缀和
res += count[prefix_sum - sum]; //前缀和为curr-targetSum的节点数
count[prefix_sum]++;
res += helper(root->left, sum, prefix_sum) + helper(root->right, sum, prefix_sum);
count[prefix_sum]--;
return res;
}
};