二叉树路径总和系列问题
二叉樹路徑總和系列問題
作者:Grey
原文地址:
博客園:二叉樹路徑總和系列問題
CSDN:二叉樹路徑總和系列問題
LeetCode 112. 路徑總和
定義遞歸函數
boolean process(TreeNode node, int preSum, int target)
遞歸含義表示:從 node 節點一直到樹的葉子節點,preSum 表示 node 之前的節點之和,target 表示目標值。主函數調用
public boolean hasPathSum(TreeNode root, int targetSum) {
return process(root, 0, targetSum);
}
就是要的答案。
接下來是遞歸函數的 base case,容易得到
if (node == null) {
// 空節點自然返回false,即永遠得不到 target 值
return false;
}
if (node.left == null && node.right == null) {
// node 是葉子節點
// 可以結算了,前面得到的值 + 當前節點值 如果符合目標值,就返回 true
// 否則返回 false
return preSum + node.val == target;
}
接下來是普遍情況,當前節點往右走,或者當前節點往左走,如果任何一個滿足條件,就返回 true 表示滿足,否則返回 false
return process(node.left, preSum + node.val, target) || process(node.right, preSum + node.val, target);
完整代碼如下
public boolean hasPathSum(TreeNode root, int targetSum) {
return process(root, 0, targetSum);
}
// 從 某個節點到 葉子節點,是否可以累計得到某個值(targetSum), 之前的值是 preSum
public boolean process(TreeNode node, int preSum, int target) {
if (node == null) {
return false;
}
if (node.left == null && node.right == null) {
// node 是葉子節點
return preSum + node.val == target;
}
return process(node.left, preSum + node.val, target) || process(node.right, preSum + node.val, target);
}
LeetCode 113. 路徑總和 II
定義遞歸函數
void process(TreeNode node, int preSum, List<Integer> path, int targetSum, List<List<Integer>> result)
遞歸含義表示:從 node 節點一直到樹的葉子節點,preSum 表示 node 之前的節點之和,targetSum 表示目標值,path 記錄之前的節點軌跡,result 用于收集所有滿足條件的 path,主函數調用:
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
if (root == null) {
return new ArrayList<>();
}
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
// 從 root 到葉子節點
// preSum 初始為 0,path 和 result 都初始為空
process(root, 0, path, targetSum, result);
return result;
}
遞歸函數的 base case 如下:
if (node == null) {
return;
}
if (node.left == null && node.right == null) {
// 葉子節點
if (preSum + node.val == targetSum) {
path.add(node.val);
result.add(path);
}
return;
}
說明:針對葉子節點(即:node.left == null && node.right == null),如果滿足條件preSum + node.val == targetSum,則找到一條滿足條件的路徑,使得結果之和等于目標值,此時 result 開始收集result.add(path)。
接下來是普遍情況,為了避免 path 在走左側和走右側的時候被污染,所以拷貝出兩個 path 的副本來進行遞歸調用
// 走左側,用copy1
List<Integer> copy1 = new ArrayList<>(path);
// 走右側,用copy2
List<Integer> copy2 = new ArrayList<>(path);
copy1.add(node.val);
copy2.add(node.val);
接下來調用遞歸函數
process(node.left, preSum + node.val, copy1, targetSum, result);
process(node.right, preSum + node.val, copy2, targetSum, result);
完整代碼如下
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
if (root == null) {
return new ArrayList<>();
}
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
process(root, 0, path, targetSum, result);
return result;
}
public void process(TreeNode node, int preSum, List<Integer> path, int targetSum, List<List<Integer>> result) {
if (node == null) {
return;
}
if (node.left == null && node.right == null) {
// 葉子節點
if (preSum + node.val == targetSum) {
path.add(node.val);
result.add(path);
}
return;
}
List<Integer> copy1 = new ArrayList<>(path);
List<Integer> copy2 = new ArrayList<>(path);
copy1.add(node.val);
copy2.add(node.val);
process(node.left, preSum + node.val, copy1, targetSum, result);
process(node.right, preSum + node.val, copy2, targetSum, result);
}
LeetCode 437. 路徑總和 III
這一題和上一個 Path Sum II 問題最大的區別是:這里不需要從根節點開始,也不需要在葉子節點結束,但是路徑方向必須是向下的(只能從父節點到子節點)。返回的結果也只需要給出路徑的條數,不需要給出具體的路徑情況。
定義遞歸函數
int process(TreeNode node, int targetSum, long preSum, HashMap<Long, Integer> preSumMap)
遞歸含義表示:從 node 到結尾,前面累計的結果是 preSum,前面組成的路徑中,每個路徑之和的結果有多少種,存在 preSumMap 中(即:preSum(i, j) 表示 前面路徑的累加和為 i 的路徑有 j 種),返回得到 targetSum 的路徑數有多少種。
所以主函數調用
process(root, targetSum, 0, preSumMap);
即為答案,先看遞歸函數完整代碼
// 返回方法數
// 從 node 到結尾,前面累計的結果是 preSum,前面組成的路徑中,每個路徑之和的結果有多少種,存在 preSumMap
// preSum(i,j) 表示 前面路徑的累加和為 i 的路徑有 j 種
public int process(TreeNode node, int targetSum, long preSum, HashMap<Long, Integer> preSumMap) {
if (node == null) {
return 0;
}
long all = preSum + node.val;
int ans = 0;
if (preSumMap.containsKey(all - targetSum)) {
// 說明之前有路徑可以得到
ans = preSumMap.get(all - targetSum);
}
// 登記當前結果到preSumMap中
if (!preSumMap.containsKey(all)) {
preSumMap.put(all, 1);
} else {
preSumMap.put(all, preSumMap.get(all) + 1);
}
ans += process(node.left, targetSum, all, preSumMap);
ans += process(node.right, targetSum, all, preSumMap);
// 清理現場
if (preSumMap.get(all) == 1) {
preSumMap.remove(all);
} else {
preSumMap.put(all, preSumMap.get(all) - 1);
}
return ans;
}
其中
if (node == null) {
return 0;
}
是 base case,空樹,返回 0 種方法數,
接下來:
long all = preSum + node.val;
int ans = 0;
if (preSumMap.containsKey(all - targetSum)) {
// 說明之前有路徑可以得到
ans = preSumMap.get(all - targetSum);
}
表示,想得到目標值 targetSum,而當前的累加和是 all,說明 preSumMap 中必須存著累加和為(all - targetSum)的記錄。
假設之前(all - targetSum)結果的路徑有 n 條,那么結果至少是 n。
接下來:
// 登記當前結果到preSumMap中
if (!preSumMap.containsKey(all)) {
preSumMap.put(all, 1);
} else {
preSumMap.put(all, preSumMap.get(all) + 1);
}
表示登記當前之和為 all 的記錄到 preSumMap 中,再接下來:
ans += process(node.left, targetSum, all, preSumMap);
ans += process(node.right, targetSum, all, preSumMap);
表示去左右樹收集答案,累加到 ans 中,最后:
// 清理現場
if (preSumMap.get(all) == 1) {
preSumMap.remove(all);
} else {
preSumMap.put(all, preSumMap.get(all) - 1);
}
遞歸函數清理現場,常規操作。
特別注意,由于遞歸函數中
long all = preSum + node.val;
int ans = 0;
if (preSumMap.containsKey(all - targetSum)) {
// 說明之前有路徑可以得到
ans = preSumMap.get(all - targetSum);
}
這里判斷條件是preSumMap.containsKey(all - targetSum),而當 preSumMap 一個元素也沒有的時候,可以組成和為 0L 的路徑,所以,在主函數調用的時候,需要增加一句
preSumMap.put(0L, 1);
完整代碼如下
public int pathSum(TreeNode root, int targetSum) {
HashMap<Long, Integer> preSumMap = new HashMap<>();
// 累加和為0的情況,默認就有一種了(空樹)
preSumMap.put(0L, 1);
return process(root, targetSum, 0, preSumMap);
}
// 返回方法數
// 從 node 到結尾,前面累計的結果是 preSum,前面組成的路徑中,每個路徑之和的結果有多少種,存在 preSumMap
// preSum(i,j) 表示 前面路徑的累加和為 i 的路徑有 j 種
public int process(TreeNode node, int targetSum, long preSum, HashMap<Long, Integer> preSumMap) {
if (node == null) {
return 0;
}
long all = preSum + node.val;
int ans = 0;
if (preSumMap.containsKey(all - targetSum)) {
// 說明之前有路徑可以得到
ans = preSumMap.get(all - targetSum);
}
// 登記當前結果到preSumMap中
if (!preSumMap.containsKey(all)) {
preSumMap.put(all, 1);
} else {
preSumMap.put(all, preSumMap.get(all) + 1);
}
ans += process(node.left, targetSum, all, preSumMap);
ans += process(node.right, targetSum, all, preSumMap);
// 清理現場
if (preSumMap.get(all) == 1) {
preSumMap.remove(all);
} else {
preSumMap.put(all, preSumMap.get(all) - 1);
}
return ans;
}
更多
算法和數據結構學習筆記
算法和數據結構學習代碼
總結
以上是生活随笔為你收集整理的二叉树路径总和系列问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 采用上一届毕业论文查重能过吗
- 下一篇: 3D 高斯点染简介