【树】B003 路径总和 III(递归 | 累加区间 | 前缀和)

一、题目描述

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

二、题解

方法一:递归

* 这题第一次做的时候让我感到最疑惑的是:

  • Q1:为什么要用多层递归。
    A1:后来才发现路径的开头可以不是根节点,结束也可以不是叶子节点。所以我们要将每一个结点作为开头地去枚举每一个结点。

其他和 I,II 还是不变。

int cnt;
public int pathSum(TreeNode root, int sum) {
  if (root == null)
      return 0;
  dfs(root, sum);
  pathSum(root.left, sum);
  pathSum(root.right, sum);
  return cnt;
}
void dfs(TreeNode root, int re) {
  if (root == null) {
      return;
  }
  if (re == root.val) {
      cnt++;
  }
  dfs(root.left, re-root.val);
  dfs(root.right, re-root.val);
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2)
  • 空间复杂度: O ( n ) O(n)

方法二:累加区间

对于 path 中的每一个结点,我们都从该结点往回枚举,查看是否有路径匹配。

  • Q1:能不能找到一条路径之后,直接终止循环?
    A1:反例是 root = [0,1,1],sum = 1,答案是 4。
  • Q2:为什么从后往前遍历列表?
    A2:因为这样保证末尾结点与之前的结点的可组合性。
    • 比如:A1 的反例,1 算一种,[1, 0] 算一种,如果从前往后遍历则会少一种。
int cnt = 0;
int sum = 0;
List<Integer> path = null;
public int pathSum(TreeNode root, int target) {
  sum = target;
  path = new ArrayList<>();
  dfs(root);
  return cnt;
}
private void dfs(TreeNode root) {
  if (root == null) {
      return;
  }
  path.add(root.val);
  int t = 0;
  for (int i = path.size()-1; i >= 0; i++) {
      t += path.get(i);
      if (t == sum) {
          cnt++;
          //break; 不能提前break [1],[1,0]
      }
  }
  dfs(root.left);
  dfs(root.right);
  path.remove(path.size()-1);
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2)
  • 空间复杂度: O ( n ) O(n)

方法二:前缀和

如果 map 中存在 cur-sum,证明我们从结点 cur-sum 开始走到当前结点的路径是一条合法路径。例如:我们知道 sum = 11 的路径。我们走到叶子结点 3,路径总和为 21,21 - 11 = 10,map 中可能会存在 10 的前缀和。

一句话:如果有前缀和和 cur - sum,而当前的和又为 cur,一定存在一条差为 cur - (cur - sum) 的路径。

      10
     / 
    5
   /
  3  
 / 
3  
int cnt = 0;
int sum = 0;
int cur = 0;
Map<Integer, Integer> map = null;

public int pathSum(TreeNode root, int target) {
   map = new HashMap<>();
   map.put(0, 1);
   sum = target;
   dfs(root);
   return cnt;
}
private void dfs(TreeNode root) {
   if (root == null) {
       return;
   }
   cur += root.val;
   cnt += map.getOrDefault(cur - sum, 0);
   map.put(cur, map.getOrDefault(cur, 0) + 1);
   dfs(root.left);
   dfs(root.right);
   // 回溯
   cur -= root.val;
   map.put(cur, map.get(cur) - 1);
}

复杂度分析

  • 时间复杂度: O ( n ) O(n)
  • 空间复杂度: O ( n ) O(n)
发布了495 篇原创文章 · 获赞 105 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/104828300