437. 路径总和 III
问题描述
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
分析
暴力解法,枚举每个结点开始是否有符合题意的路径,需要dfs枚举起点,从起点开始枚举路径还是dfs,所以是两层dfs。值得注意的是,因为题目中有负数,限制了剪枝:
- 当当前路径结点和大于target时,不能return,因为下一个结点可能是负数,会使得当前路径和再减小
- 当找到res时,不能return,因为下一个结点可能是1,再下一个结点可能是-1,于是还有答案
法一、暴力枚举,两次递归
class Solution {
public:
long long res = 0;
int target_sum = 0;
// 从当前结点开始寻找答案路径
void solve(TreeNode* root, long long x) {
if (root == nullptr) {
return ;
}
x += root->val;
// if (x > target_sum) {
// return ;
// }
if (x == target_sum) {
res++;
// return ;
}
solve(root->left, x);
solve(root->right, x);
}
void dfs(TreeNode* root) {
if (root == nullptr) {
return ;
}
solve(root, 0);
dfs(root->left);
dfs(root->right);
}
int pathSum(TreeNode* root, int targetSum) {
this->target_sum = targetSum;
dfs(root);
return res;
}
};
法二、前缀和+dfs
暴力方法中存在着许多重复计算,判断路径和是否等于某个值,显然想到前缀和。
用um记录在当前路径上从根结点开始的前缀和,由于cur_sum - prefix = sum,所以dfs到每个结点时,检索cur_sum - sum的数量即可,加到res中。而um就是存放当前路径上每个前缀和出现的次数。
class Solution {
public:
unordered_map<long long, int> um; // <在该路径上根结点开始的路径和,出现次数>
int res = 0;
int target_sum = 0;
void solve(TreeNode* root, long long cur_sum) {
if (root == nullptr) {
return ;
}
cur_sum += root->val;
if (um.count(cur_sum - target_sum) != 0) { // 注意,这里的顺序,应该先判断res,然后再把当前cur_sum插入到um
res += um[cur_sum-target_sum];
}
if (um.count(cur_sum) == 0) {
um.insert(make_pair(cur_sum, 1));
} else {
um[cur_sum]++;
}
solve(root->left, cur_sum);
solve(root->right, cur_sum);
um[cur_sum]--; // 回溯
}
int pathSum(TreeNode* root, int targetSum) {
this->target_sum = targetSum;
um.insert(make_pair(0,1));
solve(root, 0);
return res;
}
};

浙公网安备 33010602011771号