Paths with Sum:在一个二叉树中计算路径上节点和等于给定sum
的路径数量。
首先想到的是暴力搜索法,我们从每一个节点出发,寻找所有的路径,看看哪些路径和为sum
。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
if(root == nullptr) return 0;
int rootCnt = pathCnt(root, sum, 0);
int leftCnt = pathSum(root->left, sum);
int rightCnt = pathSum(root->right, sum);
return rootCnt + leftCnt + rightCnt;
}
private:
int pathCnt(TreeNode* root, const int sum, int part)
{
int paths = 0;
if(root == nullptr) return 0;
part += root->val;
if(part == sum) paths++;
paths += pathCnt(root->left, sum, part);
paths += pathCnt(root->right, sum, part);
return paths;
}
};
算法的复杂度计算会复杂一些,考虑每个节点被访问了多少次。如果是完全二叉树,在第1
层遍历N - 1
个节点,第2
层遍历2(N - 1)
个几点,第3
层遍历4(N - 3)
个节点,通向公式为k(N - 2 ^ k + 1)
,所以总的复杂度应该为O(N * logN * logN)
。书上因为没有外边的k
,所以通项公式为(N - 2 ^ k + 1)
,总复杂度为O(NlogN)
。如果不是完全二叉树,结果可能会好一些。
在上面的解法中,每一条从根到叶的路径中的所有子路径都会被重新遍历一次,这是导致时间复杂度升高的原因。如果可以在遍历一条最长的路径中,也同时计算子路径是否满足所求,就可以降低复杂度,因此可以借鉴“在数组中计算所有累加和为指定值的子数组数量”的解法。
在遍历一条从根到叶的路径时,不断计算从根到当前的累加和,并查找之前的累加和中是否存在part - sum
,以此来计算路径数量
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
hashSum.insert(pair<int, int>(0, 1));
pathCnt(root, sum);
return cnt;
}
private:
int cnt = 0, part = 0;
unordered_map<int, int> hashSum;
void pathCnt(TreeNode* root, const int sum)
{
if(root == nullptr) return;
part += root->val;
unordered_map<int, int>::iterator iter;
if((iter = hashSum.find(part - sum)) != hashSum.end()){
cnt += iter->second;
}
hashSum[part]++;
pathCnt(root->left, sum);
pathCnt(root->right, sum);
hashSum[part]--;
if(hashSum[part] == 0) hashSum.erase(part);
part -= root->val;
}
};