例题

1.路径总和 I

给你二叉树的根节点root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回true ;否则,返回 false
叶子节点 是指没有子节点的节点。
示例: :::info 给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。 :::

思路

采用深度优先搜索遍历 DFS ,每经历过一个节点,目标值删除掉当前节点值。
当 当前节点为叶子节点时,目标值如果等于当前节点值,则返回true,否则返回false

  1. var hasPathSum = function(root, targetSum) {
  2. if (!root) return false;
  3. if (!root.left && !root.right) {
  4. return targetSum === root.val
  5. }
  6. return (
  7. hasPathSum(root.left, targetSum - root.val) ||
  8. hasPathSum(root.right, targetSum - root.val)
  9. )
  10. };

2.路径总和 II

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。 :::info 给定如下二叉树,以及目标和 sum = 22.
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回: [
[5,4,11,2],
[5,8,4,5]
] :::

思路

本道题是在上述的 路径总和 I 的基础上去进行解答。
定义一个收集路径的数组arr,在每经历过一个节点的时候,将其加入到数组arr中。当遍历到叶子节点的时候,判断目标值是否与当前节点值相等,如果相等则浅拷贝后加入到结果数组res中,然后arr数组再次弹出当前节点值即可。

  1. var pathSum = function(root, targetSum) {
  2. let res = []
  3. const dfs = function (root, arr, target) {
  4. if (!root) return;
  5. arr.push(root.val)
  6. if (target === root.val && !root.left && !root.right) {
  7. res.push(arr.slice())
  8. }
  9. root.left && dfs(root.left, arr, target - root.val)
  10. root.right && dfs(root.right, arr, target - root.val)
  11. arr.pop()
  12. }
  13. dfs(root, [], targetSum)
  14. return res
  15. };

3.路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例: :::info root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
返回 3。和等于 8 的路径有:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11 :::

思路

  • 递归

统计统计三个值:

  1. 以 node 为起点,包含这个 node 本身加起来等于 sum 的路径数量之和。
  2. 以 node.left 为起点,等于 sum 的路径数量之和。
  3. 以 node.right 为起点,等于 sum 的路径数量之和。 ```javascript let pathSum = function (root, sum) { if (!root) return 0 return ( countSum(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum) ) }

let countSum = (node, sum) => { let count = 0 let dfs = (node, target) => { if (!node) return

  1. // 这里拿到结果不能直接return 要继续向下考虑
  2. // 比如下面的两个节点是 1, -1 那么它们相加得 0 也能得到一个路径
  3. if (node.val === target) {
  4. count += 1
  5. }
  6. dfs(node.left, target - node.val)
  7. dfs(node.right, target - node.val)

} dfs(node, sum) return count }

  1. - **前缀和**
  2. 在上面的递归思路中,时间复杂度过于高,我们可以采用前缀和的思路。<br />具体思路可以借鉴[前缀和的运用,一步步优化](https://leetcode.cn/problems/subarray-sum-equals-k/solution/dai-ni-da-tong-qian-zhui-he-cong-zui-ben-fang-fa-y/),讲得很好
  3. ```javascript
  4. var pathSum = function(root, targetSum) {
  5. const prefix = new Map()
  6. // 初始化空路径
  7. prefix.set(0, 1)
  8. return dfs(root, prefix, targetSum, 0)
  9. };
  10. // 前缀和
  11. const dfs = function(root, prefix, targetSum, curr) {
  12. if(!root) return 0
  13. let ret = 0
  14. curr += root.val
  15. ret = prefix.get(curr - targetSum) || 0
  16. prefix.set(curr, (prefix.get(curr) || 0) + 1)
  17. ret += dfs(root.left, prefix, targetSum, curr)
  18. ret += dfs(root.right, prefix, targetSum, curr)
  19. // 当前节点遍历调用后减一
  20. prefix.set(curr, (prefix.get(curr) || 0) - 1)
  21. return ret
  22. }