welcome to my blog
LeetCode Top 100 Liked Questions 437. Path Sum III (Java版; Easy)
题目描述
You are given a binary tree in which each node contains an integer value.
Find the number of paths that sum to a given value.
The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.
Example:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
Return 3. The paths that sum to 8 are:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
class Solution {
public int pathSum(TreeNode root
, int sum
) {
if(root
==null
){
return 0;
}
HashMap
<Integer, Integer> map
= new HashMap<>();
map
.put(0, 1);
return backtrace(map
, root
, 0, sum
);
}
private int backtrace(HashMap
<Integer, Integer> map
, TreeNode root
, int cur
, int sum
){
if(root
==null
){
return 0;
}
int res
= 0;
cur
= cur
+ root
.val
;
if(map
.containsKey(cur
- sum
)){
res
= map
.get(cur
- sum
);
}
map
.put(cur
, map
.getOrDefault(cur
, 0)+1);
res
= res
+ backtrace(map
, root
.left
, cur
, sum
);
res
= res
+ backtrace(map
, root
.right
, cur
, sum
);
map
.put(cur
, map
.get(cur
)-1);
return res
;
}
}
class Solution {
public int pathSum(TreeNode root
, int sum
) {
HashMap
<Integer, Integer> map
= new HashMap<>();
map
.put(0, 1);
return core(map
, root
, 0, sum
);
}
private int core(HashMap
<Integer, Integer> map
, TreeNode root
, int curSum
, int sum
){
if(root
==null
){
return 0;
}
curSum
= curSum
+ root
.val
;
int res
= map
.getOrDefault(curSum
- sum
, 0);
map
.put(curSum
, map
.getOrDefault(curSum
, 0)+1);
res
+= core(map
, root
.left
, curSum
, sum
);
res
+= core(map
, root
.right
, curSum
, sum
);
map
.put(curSum
, map
.get(curSum
)-1);
return res
;
}
}
第一次做; 最优解; 使用哈希表记录前缀和; 突然想起一句话:已背; 看注释
-时间复杂度O(N)
int res = preSum.getOrDefault(currSum - target, 0)这句太秀了! 极致地利用了前缀和
比如1→2,target=3; 当前在2处,currSum=3,preSum包含3-3=0这个key, 说明从root到当前节点这条路上存在满足条件的路径
比如1→2,target=2; 当前在2处,currSum=3,preSum包含3-2=1这个key, 说明从root到当前节点这条路上存在满足条件的路径
比如1→1→2,target=2; 当前在2处,currSum=4,preSum包含4-2=2这个key, 说明从root到当前节点这条路上存在满足条件的路径
import java
.util
.HashMap
;
class Solution {
public int pathSum(TreeNode root
, int sum
) {
if(root
==null
)
return 0;
HashMap
<Integer,Integer> prefixSum
= new HashMap<>();
prefixSum
.put(0,1);
return core(prefixSum
, sum
, root
, 0);
}
public int core(HashMap
<Integer,Integer> prefixSum
, int target
, TreeNode root
, int currSum
){
if(root
==null
)
return 0;
currSum
= currSum
+ root
.val
;
int res
= prefixSum
.getOrDefault(currSum
-target
, 0);
prefixSum
.put(currSum
, prefixSum
.getOrDefault(currSum
,0) + 1);
int leftRes
= core(prefixSum
, target
, root
.left
, currSum
);
int rightRes
= core(prefixSum
, target
, root
.right
, currSum
);
prefixSum
.put(currSum
, prefixSum
.get(currSum
)-1);
return res
+ leftRes
+ rightRes
;
}
}
第二次做; 在二叉树中使用哈希表前缀和方法一定要记得恢复现场; 哈希表前缀和方法中一定需要的条件:哈希表, preSum, 如果是涉及路经的长度,则还需要元素的位置信息; 本题中的三个核心:1) 初始化sumMap, sumMap.put(0,1), 这是为了找出以根节点开始的情况 2)先判断sumMap中是否有curSum-k, 再把curSum加入到sumMap中 3)恢复现场, 考虑完当前节点的情况后, 要把当前节点对哈希表的改动撤回
class Solution {
public int pathSum(TreeNode root
, int sum
) {
if(root
==null
)
return 0;
HashMap
<Integer, Integer> sumMap
= new HashMap<>();
sumMap
.put(0, 1);
return core(sumMap
, root
, 0, sum
);
}
public int core(HashMap
<Integer, Integer> sumMap
, TreeNode root
, int pre
, int k
){
if(root
==null
)
return 0;
int curSum
= pre
+ root
.val
;
int res
= sumMap
.getOrDefault(curSum
-k
, 0);
sumMap
.put(curSum
, sumMap
.getOrDefault(curSum
, 0)+1);
int leftRes
= core(sumMap
, root
.left
, curSum
, k
);
int rightRes
= core(sumMap
, root
.right
, curSum
, k
);
sumMap
.put(curSum
, sumMap
.get(curSum
)-1);
return res
+ leftRes
+ rightRes
;
}
}
第一次做; 双重递归, 要理解每个递归的作用, 第一个递归:以不同的节点作为路径的开始; 第二个递归:从给定的开始节点向下探索是否存在符合条件的路径; 本题还有个大陷阱:满足条件时不能直接返回,而是要继续向下探索,见注释
class Solution {
public int pathSum(TreeNode root
, int sum
) {
if(root
==null
)
return 0;
int currRes
= core(root
, sum
);
int leftRes
= pathSum(root
.left
, sum
);
int rightRes
= pathSum(root
.right
, sum
);
return currRes
+ leftRes
+ rightRes
;
}
public int core(TreeNode root
, int sum
){
if(root
==null
)
return 0;
int curr
= 0;
if(root
.val
== sum
)
curr
= 1;
int leftRes
= core(root
.left
, sum
-root
.val
);
int rightRes
= core(root
.right
, sum
-root
.val
);
return curr
+ leftRes
+ rightRes
;
}
}
LeetCode最优解; 使用哈希表, key是前缀和, value是某个和出现的次数, 核心:从root开始记录, 到当前节点为止
结合这个例子理解:
For instance : in one path we have 1,2,-1,-1,2, then the prefix sum will be: 1, 3, 2, 1, 3, let's say we want to find target sum is 2,
then we will have{2}, {1,2,-1}, {2,-1,-1,2} and {2}ways.
思路分析:
This is an excellent idea and took me some time to figure out the logic behind.
Hope my comment here could help understanding this solution.
The prefix stores the sum from the root to the current node in the recursion
The map stores <prefix sum, frequency> pairs before getting to the current node. We can imagine a path from the root to the current node.
The sum from any node in the middle of the path to the current node = the difference between the sum from the root to the current node and
the prefix sum of the node in the middle.
We are looking for some consecutive nodes that sum up to the given target value, which means the difference discussed in 2. should equal
to the target value. In addition, we need to know how many differences are equal to the target value.
Here comes the map. The map stores the frequency of all possible sum in the path to the current node. If the difference between the current
sum and the target value exists in the map, there must exist a node in the middle of the path, such that from this node to the current node,
the sum is equal to the target value.
Note that there might be multiple nodes in the middle that satisfy what is discussed in 4. The frequency in the map is used to help with this.
Therefore, in each recursion, the map stores all information we need to calculate the number of ranges that sum up to target. Note that each
range starts from a middle node, ended by the current node.
To get the total number of path count, we add up the number of valid paths ended by EACH node in the tree.
Each recursion returns the total count of valid paths in the subtree rooted at the current node. And this sum can be divided into three parts:
- the total number of valid paths in the subtree rooted at the current node's left child
- the total number of valid paths in the subtree rooted at the current node's right child
- the number of valid paths ended by the current node
The interesting part of this solution is that the prefix is counted from the top(root) to the bottom(leaves), and the result of total count
is calculated from the bottom to the top :D
The code below takes 16 ms which is super fast.
补充说明:
I just wanted to add one additional point, which took me some time to think through. I was wondering how you can keep track of a sequence of
sums in a 1D hash table, when a tree can have multiple branches, how do you keep track of duplicate sums on different branches of the tree?
The answer is that this method only keeps track of 1 branch at a time. Because we're doing a depth first search, we will iterate all the way
to the end of a single branch before coming back up. However, as we're coming back up, we're removing the nodes at the bottom of the branch
from our hash table, using line map.put(sum, map.get(sum) - 1);, before ending the function.
To summarize, the hash table is only keeping track of a portion of a single branch at any given time, from the root node to the current node only.
public int pathSum(TreeNode root
, int sum
) {
if (root
== null
) {
return 0;
}
Map
<Integer, Integer> map
= new HashMap<>();
map
.put(0, 1);
return findPathSum(root
, 0, sum
, map
);
}
private int findPathSum(TreeNode curr
, int sum
, int target
, Map
<Integer, Integer> map
) {
if (curr
== null
) {
return 0;
}
sum
+= curr
.val
;
int numPathToCurr
= map
.getOrDefault(sum
-target
, 0);
map
.put(sum
, map
.getOrDefault(sum
, 0) + 1);
int res
= numPathToCurr
+ findPathSum(curr
.left
, sum
, target
, map
)
+ findPathSum(curr
.right
, sum
, target
, map
);
map
.put(sum
, map
.get(sum
) - 1);
return res
;
}