437.路径总和 III
题目描述
给定一个二叉树的根节点 root
,和一个整数 targetSum
,求该二叉树里节点值之和等于 targetSum
的 路径 的数目。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
题目理解
因为题目规定了路径方向必须是向下的,所以我们可以按照深度优先遍历的方法遍历二叉树的每一条路径,然后在每一条路径上查找我们所要求的路径。
因此问题可以被分解为求一个个的子问题,子问题为求解A0 - A1 - A2 - ... - An
的一条路径中,满足节点之和等于targetSum
的路径。
再定义前缀和的概念:由根节点到当前节点的路径上所有节点的和。
假设上述子问题中,A0
为根节点,那么:
A0
的前缀和prefix(A0) = A0
A1
的前缀和prefix(A1) = A0+A1
A2
的前缀和prefix(A2) = A0+A1+A2
- …
An
的前缀和prefix(An) = A0+A1+...+An
任意两个节点之间的节点和就等于:
p
r
e
f
i
x
(
A
k
)
−
p
r
e
f
i
x
(
A
j
)
=
A
j
+
1
+
A
j
+
2
+
.
.
.
+
A
k
,
k
>
j
prefix(A_k)-prefix(A_j)=A_{j+1}+A_{j+2}+...+A_{k},\space \space \space \space k>j
prefix(Ak)−prefix(Aj)=Aj+1+Aj+2+...+Ak, k>j
回到之前的问题
我们要求解A0 - A1 - A2 - ... - An
的一条路径中,满足节点之和等于targetSum
的路径,等价于求解
p
r
e
f
i
x
(
A
k
)
−
p
r
e
f
i
x
(
A
j
)
=
=
t
a
r
g
e
t
S
u
m
,
j
=
[
0
,
k
−
1
]
prefix(A_k)-prefix(A_j)==targetSum,\space \space \space \space j=[0,k-1]
prefix(Ak)−prefix(Aj)==targetSum, j=[0,k−1]
这个公式的意思是:节点
A
j
+
1
+
A
j
+
2
+
.
.
.
+
A
k
A_{j+1}+A_{j+2}+...+A_{k}
Aj+1+Aj+2+...+Ak的和等于targetSum
的路径,那么根据j
的取值范围,我们遍历了多少条路径呢?大概写一下:
- A 1 + A 2 + A 3 + . . . A k A_1+A_2+A_3+...A_k A1+A2+A3+...Ak
- A 2 + A 3 + . . . A k A_2+A_3+...A_k A2+A3+...Ak
- A 3 + . . . A k A_3+...A_k A3+...Ak
- A k − 1 + A k A_{k-1}+A_k Ak−1+Ak
通过观察发现,我们少了一条路径没有遍历,那就是
A
0
+
A
1
+
A
2
+
A
3
+
.
.
.
A
k
A_0+A_1+A_2+A_3+...A_k
A0+A1+A2+A3+...Ak,此时的targetSum
是等于
p
r
e
f
i
x
(
A
k
)
prefix(A_k)
prefix(Ak)的,所以要单独处理这种情况,即在A0
节点之前虚拟一个节点另其前缀和为0。
伪代码
我们在遍历二叉树的时候要维护一个哈希表,集合当前路径上前缀和与前缀和节点数量的映射。注:因为节点有正负,所以不同的节点可以具有不同的前缀和。
1. 更新当前节点的前缀和
2. 根据当前节点的前缀和在map中查找符合条件的路径数量
3. 更新map
4. 递归进入左子树
5. 递归进入右子树
代码
class Solution {
unordered_map<long long, int>prefix;
public:
int pathSum(TreeNode* root, int targetSum) {
prefix[0] = 1; // 特殊情况
int ans = DFS(root, 0, targetSum);
return ans;
}
int DFS(TreeNode* root, long long prefixSum, int targetSum){
if(root == nullptr)
return 0;
long long cur_prefix = prefixSum + root->val;
int ret = 0;
if(prefix.count(cur_prefix - targetSum)){
ret = prefix[cur_prefix - targetSum];
}
prefix[cur_prefix]++;
int ret_left = DFS(root->left, cur_prefix, targetSum);
int ret_right = DFS(root->right, cur_prefix, targetSum);
prefix[cur_prefix]--;
return ret + ret_left + ret_right;
}
};