题目描述: 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例1:
1 | 输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 |
示例2:
1 | 输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 |
题解方法一:前缀和+递归+回溯
前缀和的递归回溯思路:
- 从当前节点反推到根节点(反推比较好理解,正向其实也只有一条),有且仅有一条路径,因为这是一棵树
- 如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
- 所以前缀和对于当前路径来说是唯一的,当前记录的前缀和,在回溯结束,回到本层时去除,保证其不影响其他分支的结果
代码实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var pathSum = function(root, targetSum) {
const prefix = new Map();
prefix.set(0, 1);
return dfs(root, prefix, 0, targetSum);
}
var dfs = (root, prefix, curr, targetSum) => {
// 1.递归终止条件
if (root == null) {
return 0;
}
// 2.本层要做的事情
let ret = 0;
curr += root.val; // 当前路径上的和
// 看看root到当前节点这条路上是否存在节点前缀和 + target = currSum的路径
// 当前节点root节点反推,有且仅有一条路径,如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
// currSum-target相当于找路径的起点,起点的sum+target=currSum,当前点到起点的距离就是target
ret = prefix.get(curr - targetSum) || 0;
prefix.set(curr, (prefix.get(curr) || 0) + 1); // 更新路径上当前节点前缀和的个数
// 3.进入下一层
ret += dfs(root.left, prefix, curr, targetSum);
ret += dfs(root.right, prefix, curr, targetSum);
// 4.回到本层,恢复状态,去除当前节点的前缀和数量
prefix.set(curr, (prefix.get(curr) || 0) - 1);
return ret;
}
我们以示例1为例,进行图解分析
执行代码第1步
1
2
3
4
5var pathSum = function(root, targetSum) {
const prefix = new Map();
prefix.set(0, 1);
return dfs(root, prefix, 0, targetSum);
}
执行代码第2步
1 |
|
执行代码第3步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var dfs = (root, prefix, curr, targetSum) => {
if (root == null) {
return 0;
}
let ret = 0; // 执行代码第3步
curr += root.val; // 执行代码第3步 curr = 10 + 5 = 15
ret = prefix.get(curr - targetSum) || 0; // 执行代码第3步 curr - targetSum = 15 - 8 = 7 ret = 0
prefix.set(curr, (prefix.get(curr) || 0) + 1); // 执行代码第3步
ret += dfs(root.left, prefix, curr, targetSum); // 执行代码第4步 ret = 1
ret += dfs(root.right, prefix, curr, targetSum); // 执行代码第10步 ret = 1+ 1 =2
prefix.set(curr, (prefix.get(curr) || 0) - 1);
return ret;
}
执行代码第4步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var dfs = (root, prefix, curr, targetSum) => {
if (root == null) {
return 0;
}
let ret = 0; // 执行代码第4步
curr += root.val; // 执行代码第4步 curr = 15 + 3 = 18
ret = prefix.get(curr - targetSum) || 0; // 执行代码第4步 curr - targetSum = 18 - 8 = 19 ret = 1
prefix.set(curr, (prefix.get(curr) || 0) + 1); // 执行代码第4步
ret += dfs(root.left, prefix, curr, targetSum); // 执行代码第5步
ret += dfs(root.right, prefix, curr, targetSum); // root.right = null 执行返回
prefix.set(curr, (prefix.get(curr) || 0) - 1); // 执行代码第9步
return ret;
}
执行代码第5步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var dfs = (root, prefix, curr, targetSum) => {
if (root == null) {
return 0;
}
let ret = 0; // 执行代码第5步
curr += root.val; // 执行代码第5步 curr = 18 + 3 = 21
ret = prefix.get(curr - targetSum) || 0; // 执行代码第5步 curr - targetSum = 21 - 8 = 13 ret = 0
prefix.set(curr, (prefix.get(curr) || 0) + 1); // 执行代码第5步
ret += dfs(root.left, prefix, curr, targetSum); // 执行代码第6步 root.left = null 直接返回 0
ret += dfs(root.right, prefix, curr, targetSum); // 执行代码第7步
prefix.set(curr, (prefix.get(curr) || 0) - 1); // 执行代码第8步
return ret;
}
执行代码第6步 root.left = null 直接返回 0
执行代码第7步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var dfs = (root, prefix, curr, targetSum) => {
if (root == null) {
return 0;
}
let ret = 0; // 执行代码第7步
curr += root.val; // 执行代码第7步 curr = 21 - 3 = 19
ret = prefix.get(curr - targetSum) || 0; // 执行代码第7步 curr - targetSum = 19 - 8 = 11 ret = 0
prefix.set(curr, (prefix.get(curr) || 0) + 1); // 执行代码第7.1步
ret += dfs(root.left, prefix, curr, targetSum);
ret += dfs(root.right, prefix, curr, targetSum);
prefix.set(curr, (prefix.get(curr) || 0) - 1); // 执行代码第7.2步
return ret;
}
执行代码第8步
第8步是在第5步方法中执行的,
执行代码第9步
第9步是在第4步方法中执行的,
执行代码第10步
第10步是在第3步方法中执行的,
1 | var dfs = (root, prefix, curr, targetSum) => { |
执行代码第11步
1 | var dfs = (root, prefix, curr, targetSum) => { |
执行完第10步之后,返回到第3步中 ret = 2
右子树同理,此处省略相关图解,执行之后 ret = 3
总结:大功告成✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️
参考链接:
https://github.com/sisterAn/JavaScript-Algorithms/issues/41