2018-11-19 02:39:46 +00:00
|
|
|
|
# [437. Path Sum III](https://leetcode.com/problems/path-sum-iii/description/)
|
|
|
|
|
# 思路
|
|
|
|
|
求二叉树中路径和等于sum的路径条数,这个路径不一定是从根开始以叶子结束,只要求从上到下就行。
|
|
|
|
|
## 思路一
|
|
|
|
|
最简单的思路,如果这条路径要求必须从根开始,那么这题只需要遍历一遍二叉树就行了。
|
|
|
|
|
因此我们考虑遍历一遍二叉树,遍历到节点node时,就将node作为根节点调用从根开始的路径和函数。则算法具体步骤如下:
|
|
|
|
|
* 递归遍历root这课树,假设现在遍历到了节点node,运行rootpathSum(node, sum);
|
|
|
|
|
* rootpathSum(node, sum)计算了所有从node开始的路径的和等于sum的路径条数;
|
|
|
|
|
* 同时用一个全局变量res对结果进行累加。
|
|
|
|
|
|
|
|
|
|
运用了两次递归,复杂度有点高。
|
|
|
|
|
实际跑出来只击败了30%多...
|
|
|
|
|
时间复杂度O(n*n)
|
|
|
|
|
|
|
|
|
|
## 思路二
|
|
|
|
|
思路一递归中还包含递归,因此时间复杂度比较高。
|
|
|
|
|
其实我们在先序遍历到某个节点node时,从根到此节点路径(设为out)上所有节点都应见过了,
|
|
|
|
|
那么我们就应该知道路径out上有多少满足条件且以node结束的子路径了。out定义成一个数组,里面存放了从root到node这条路径的所有节点,再用curSum记录out里面所有
|
|
|
|
|
元素的和,每次都要检查out里面是否存在满足条件且以node结束的子路径(一个for循环)。
|
|
|
|
|
相对于思路一来说,思路二把思路一的递归改成了循环,运行时间有所下降(击败了80%的人)但是时间复杂度依然是O(n*n)
|
|
|
|
|
|
|
|
|
|
## 思路三*
|
|
|
|
|
不难发现思路二中的每次for循环包含了大量的重复运算,我们可以考虑用空间换时间的思想去掉这些不必要的重复计算。
|
|
|
|
|
大致思想还是同思路二,不过不用out数组记录root到node的所有节点,而是用一个hash表mp记录root到(当前)所有节点的每个路径和的个数,这样的话mp[curSum - sum]
|
|
|
|
|
就是满足条件的且以node结束的路径条数。
|
2018-11-19 03:21:37 +00:00
|
|
|
|
例如,若root到node的值分别是:1、2、3、-3,则4个从root开始的路径和分别为1、3、6、3,那么此时就有`mp[0] = 1`、`mp[1] = 1`、`mp[3]=2`、`mp[6]=1`。
|
|
|
|
|
若sum=2,则此时得到的满足条件的且以node结束的路径条数就为`mp[curSum - sum] = mp[3 - 2] = 1`, 这条路径就是“2、3、-3”。
|
2018-11-19 02:39:46 +00:00
|
|
|
|
此时击败了99%的人,时间复杂度为O(n)
|
|
|
|
|
|
|
|
|
|
# C++
|
|
|
|
|
## 思路一
|
|
|
|
|
``` C++
|
|
|
|
|
class Solution {
|
|
|
|
|
private:
|
|
|
|
|
int res = 0;
|
|
|
|
|
int rootpathSum(TreeNode *root, int sum){ // 计算从根开始的路径和等于sum的路径条数
|
|
|
|
|
if(!root) return 0;
|
|
|
|
|
int tmp = 0;
|
|
|
|
|
if(root -> val == sum) tmp++;
|
|
|
|
|
tmp += rootpathSum(root -> left, sum - root -> val);
|
|
|
|
|
tmp += rootpathSum(root -> right, sum - root -> val);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
void sum_rootpathSum(TreeNode* root, int sum){ // 遍历一遍树,将所有的rootpathSum相加
|
|
|
|
|
if(!root) return;
|
|
|
|
|
res += rootpathSum(root, sum);
|
|
|
|
|
sum_rootpathSum(root -> left, sum);
|
|
|
|
|
sum_rootpathSum(root -> right, sum);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
int pathSum(TreeNode* root, int sum) {
|
|
|
|
|
sum_rootpathSum(root, sum);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
## 思路二
|
|
|
|
|
``` C++
|
|
|
|
|
class Solution {
|
|
|
|
|
private:
|
|
|
|
|
int res = 0;
|
|
|
|
|
void helper(TreeNode* node, int sum, int curSum, vector<TreeNode*>& out) {
|
|
|
|
|
if (!node) return;
|
|
|
|
|
curSum += node->val;
|
|
|
|
|
out.push_back(node);
|
|
|
|
|
if (curSum == sum) ++res;
|
|
|
|
|
int t = curSum;
|
|
|
|
|
for (int i = 0; i < out.size() - 1; i++) { // 循环判断以node为结束的子路径中是否有满足条件的
|
|
|
|
|
t -= out[i]->val;
|
|
|
|
|
if (t == sum) res++;
|
|
|
|
|
}
|
|
|
|
|
helper(node->left, sum, curSum, out);
|
|
|
|
|
helper(node->right, sum, curSum, out);
|
|
|
|
|
out.pop_back();
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
int pathSum(TreeNode* root, int sum) {
|
|
|
|
|
vector<TreeNode*> out;
|
|
|
|
|
helper(root, sum, 0, out);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
## 思路三
|
|
|
|
|
``` C++
|
|
|
|
|
class Solution {
|
|
|
|
|
private:
|
|
|
|
|
int helper(TreeNode* node, int sum, int curSum, unordered_map<int, int>& mp) {
|
|
|
|
|
if (!node) return 0;
|
|
|
|
|
curSum += node->val;
|
|
|
|
|
int res = mp[curSum - sum]; // 以node为结束的路径和等于sum的路径条数
|
|
|
|
|
mp[curSum]++; // mp记录root到(当前)所有节点的路径和的个数
|
|
|
|
|
res += helper(node->left, sum, curSum, mp) + helper(node->right, sum, curSum, mp);
|
|
|
|
|
mp[curSum]--;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
int pathSum(TreeNode* root, int sum) {
|
|
|
|
|
unordered_map<int, int>mp;
|
|
|
|
|
mp[0] = 1;
|
|
|
|
|
return helper(root, sum, 0, mp);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|