Skip to content

Commit c590e2b

Browse files
authored
feat: add solutions to lcci problem: No.04.12 (#1387)
No.04.12.Paths with Sum
1 parent d3e83c1 commit c590e2b

File tree

8 files changed

+439
-281
lines changed

8 files changed

+439
-281
lines changed

lcci/04.12.Paths with Sum/README.md

Lines changed: 155 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -34,79 +34,156 @@
3434

3535
<!-- 这里可写通用的实现逻辑 -->
3636

37-
DFS 深度优先搜索
37+
**方法一:哈希表 + 前缀和 + 递归**
38+
39+
我们可以运用前缀和的思想,对二叉树进行递归遍历,同时用哈希表 $cnt$ 统计从根节点到当前节点的路径上各个前缀和出现的次数。
40+
41+
我们设计一个递归函数 $dfs(node, s)$,表示当前遍历到的节点为 $node$,从根节点到当前节点的路径上的前缀和为 $s$。函数的返回值是统计以 $node$ 节点及其子树节点作为路径终点且路径和为 $sum$ 的路径数目。那么答案就是 $dfs(root, 0)$。
42+
43+
函数 $dfs(node, s)$ 的递归过程如下:
44+
45+
- 如果当前节点 $node$ 为空,则返回 $0$。
46+
- 计算从根节点到当前节点的路径上的前缀和 $s$。
47+
- 用 $cnt[s - sum]$ 表示以当前节点为路径终点且路径和为 $sum$ 的路径数目,其中 $cnt[s - sum]$ 即为 $cnt$ 中前缀和为 $s - sum$ 的个数。
48+
- 将前缀和 $s$ 的计数值加 $1$,即 $cnt[s] = cnt[s] + 1$。
49+
- 递归地遍历当前节点的左右子节点,即调用函数 $dfs(node.left, s)$ 和 $dfs(node.right, s)$,并将它们的返回值相加。
50+
- 在返回值计算完成以后,需要将当前节点的前缀和 $s$ 的计数值减 $1$,即执行 $cnt[s] = cnt[s] - 1$。
51+
- 最后返回答案。
52+
53+
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是二叉树的节点个数。
3854

3955
<!-- tabs:start -->
4056

4157
### **Python3**
4258

4359
<!-- 这里可写当前语言的特殊实现逻辑 -->
4460

45-
采用递归的思想,每递归到某个节点时:
46-
47-
-`root.val-sum == 0`,结果加 1
48-
- 考虑将此节点纳入或不纳入路径两种情况
61+
```python
62+
# Definition for a binary tree node.
63+
# class TreeNode:
64+
# def __init__(self, x):
65+
# self.val = x
66+
# self.left = None
67+
# self.right = None
4968

50-
特殊情况:若此节点的父节点在路径中,此节点必纳入路径(路径不能断)
5169

52-
```python
5370
class Solution:
5471
def pathSum(self, root: TreeNode, sum: int) -> int:
55-
def dfs(root, sum, flag):
56-
nonlocal ans
57-
if not root:
72+
def dfs(root: TreeNode, s: int):
73+
if root is None:
5874
return 0
59-
if sum - root.val == 0:
60-
ans += 1
61-
if flag == 0:
62-
dfs(root.left, sum, 0)
63-
dfs(root.right, sum, 0)
64-
dfs(root.left, sum - root.val, 1)
65-
dfs(root.right, sum - root.val, 1)
66-
67-
if not root:
68-
return 0
69-
ans = 0
70-
dfs(root, sum, 0)
71-
return ans
75+
s += root.val
76+
ans = cnt[s - sum]
77+
cnt[s] += 1
78+
ans += dfs(root.left, s)
79+
ans += dfs(root.right, s)
80+
cnt[s] -= 1
81+
return ans
82+
83+
cnt = Counter({0: 1})
84+
return dfs(root, 0)
7285
```
7386

7487
### **Java**
7588

7689
<!-- 这里可写当前语言的特殊实现逻辑 -->
7790

78-
使用到 2 个递归过程:
79-
80-
- BFS:(traverse)遍历每个树节点;
81-
- DFS: 从每个树节点出发,节点求和,看是否能满足 sum。
82-
83-
需要注意,节点值有正有负,需要穷尽所有的可能路径。
84-
8591
```java
92+
/**
93+
* Definition for a binary tree node.
94+
* public class TreeNode {
95+
* int val;
96+
* TreeNode left;
97+
* TreeNode right;
98+
* TreeNode(int x) { val = x; }
99+
* }
100+
*/
86101
class Solution {
87-
int ans = 0;
102+
private Map<Long, Integer> cnt = new HashMap<>();
103+
private int target;
104+
88105
public int pathSum(TreeNode root, int sum) {
89-
traverse(root, sum);
90-
return ans;
106+
cnt.put(0L, 1);
107+
target = sum;
108+
return dfs(root, 0);
91109
}
92110

93-
void traverse(TreeNode root, int sum) {
94-
if (root == null) return;
95-
ans += dfs(root, sum, 0);
96-
traverse(root.left, sum);
97-
traverse(root.right, sum);
111+
private int dfs(TreeNode root, long s) {
112+
if (root == null) {
113+
return 0;
114+
}
115+
s += root.val;
116+
int ans = cnt.getOrDefault(s - target, 0);
117+
cnt.merge(s, 1, Integer::sum);
118+
ans += dfs(root.left, s);
119+
ans += dfs(root.right, s);
120+
cnt.merge(s, -1, Integer::sum);
121+
return ans;
98122
}
123+
}
124+
```
125+
126+
### **C++**
99127

100-
// check if sum of path is sum.
101-
int dfs(TreeNode root, int sum, int cur) {
102-
if (root == null) return 0;
103-
cur += root.val;
104-
int res = 0;
105-
if (cur == sum) res++;
106-
res += dfs(root.left, sum, cur);
107-
res += dfs(root.right, sum, cur);
108-
return res;
128+
```cpp
129+
/**
130+
* Definition for a binary tree node.
131+
* struct TreeNode {
132+
* int val;
133+
* TreeNode *left;
134+
* TreeNode *right;
135+
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
136+
* };
137+
*/
138+
class Solution {
139+
public:
140+
int pathSum(TreeNode* root, int sum) {
141+
unordered_map<long long, int> cnt;
142+
cnt[0] = 1;
143+
function<int(TreeNode*, long long)> dfs = [&](TreeNode* root, long long s) {
144+
if (!root) {
145+
return 0;
146+
}
147+
s += root->val;
148+
int ans = cnt[s - sum];
149+
++cnt[s];
150+
ans += dfs(root->left, s);
151+
ans += dfs(root->right, s);
152+
--cnt[s];
153+
return ans;
154+
};
155+
return dfs(root, 0);
109156
}
157+
};
158+
```
159+
160+
### **Go**
161+
162+
```go
163+
/**
164+
* Definition for a binary tree node.
165+
* type TreeNode struct {
166+
* Val int
167+
* Left *TreeNode
168+
* Right *TreeNode
169+
* }
170+
*/
171+
func pathSum(root *TreeNode, sum int) int {
172+
cnt := map[int]int{0: 1}
173+
var dfs func(*TreeNode, int) int
174+
dfs = func(root *TreeNode, s int) int {
175+
if root == nil {
176+
return 0
177+
}
178+
s += root.Val
179+
ans := cnt[s-sum]
180+
cnt[s]++
181+
ans += dfs(root.Left, s)
182+
ans += dfs(root.Right, s)
183+
cnt[s]--
184+
return ans
185+
}
186+
return dfs(root, 0)
110187
}
111188
```
112189

@@ -127,23 +204,22 @@ class Solution {
127204
* }
128205
*/
129206

130-
function dfs(root: TreeNode | null, sum: number): number {
131-
let res = 0;
132-
if (root == null) {
133-
return res;
134-
}
135-
sum -= root.val;
136-
if (sum === 0) {
137-
res++;
138-
}
139-
return res + dfs(root.left, sum) + dfs(root.right, sum);
140-
}
141-
142207
function pathSum(root: TreeNode | null, sum: number): number {
143-
if (root == null) {
144-
return 0;
145-
}
146-
return dfs(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
208+
const cnt: Map<number, number> = new Map();
209+
cnt.set(0, 1);
210+
const dfs = (root: TreeNode | null, s: number): number => {
211+
if (!root) {
212+
return 0;
213+
}
214+
s += root.val;
215+
let ans = cnt.get(s - sum) ?? 0;
216+
cnt.set(s, (cnt.get(s) ?? 0) + 1);
217+
ans += dfs(root.left, s);
218+
ans += dfs(root.right, s);
219+
cnt.set(s, (cnt.get(s) ?? 0) - 1);
220+
return ans;
221+
};
222+
return dfs(root, 0);
147223
}
148224
```
149225

@@ -170,38 +246,26 @@ function pathSum(root: TreeNode | null, sum: number): number {
170246
// }
171247
use std::rc::Rc;
172248
use std::cell::RefCell;
173-
use std::collections::VecDeque;
249+
use std::collections::HashMap;
174250
impl Solution {
175-
fn dfs(root: &Option<Rc<RefCell<TreeNode>>>, mut sum: i32) -> i32 {
176-
let mut res = 0;
177-
if root.is_none() {
178-
return res;
179-
}
180-
let root = root.as_ref().unwrap().borrow();
181-
sum -= root.val;
182-
if sum == 0 {
183-
res += 1;
184-
}
185-
res + Self::dfs(&root.left, sum) + Self::dfs(&root.right, sum)
251+
pub fn path_sum(root: Option<Rc<RefCell<TreeNode>>>, sum: i32) -> i32 {
252+
let mut cnt = HashMap::new();
253+
cnt.insert(0, 1);
254+
return Self::dfs(root, sum, 0, &mut cnt);
186255
}
187256

188-
pub fn path_sum(root: Option<Rc<RefCell<TreeNode>>>, sum: i32) -> i32 {
189-
let mut queue = VecDeque::new();
190-
if root.is_some() {
191-
queue.push_back(root);
192-
}
193-
let mut res = 0;
194-
while let Some(mut root) = queue.pop_front() {
195-
res += Self::dfs(&root, sum);
196-
let mut root = root.as_mut().unwrap().borrow_mut();
197-
if root.left.is_some() {
198-
queue.push_back(root.left.take());
199-
}
200-
if root.right.is_some() {
201-
queue.push_back(root.right.take());
202-
}
257+
fn dfs(root: Option<Rc<RefCell<TreeNode>>>, sum: i32, s: i32, cnt: &mut HashMap<i32, i32>) -> i32 {
258+
if let Some(node) = root {
259+
let node = node.borrow();
260+
let s = s + node.val;
261+
let mut ans = *cnt.get(&(s - sum)).unwrap_or(&0);
262+
*cnt.entry(s).or_insert(0) += 1;
263+
ans += Self::dfs(node.left.clone(), sum, s, cnt);
264+
ans += Self::dfs(node.right.clone(), sum, s, cnt);
265+
*cnt.entry(s).or_insert(0) -= 1;
266+
return ans;
203267
}
204-
res
268+
return 0;
205269
}
206270
}
207271
```

0 commit comments

Comments
 (0)