437. 路径总和 III

给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
示例:

 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 -> 1

思路一:双重递归

写法一:

 1 class Solution {
 2     public boolean flag = false;
 3     public int cnt = 0, cntSum = 0;
 4     public int pathSum(TreeNode root, int sum) {
 5        preTraversal(root, sum);
 6        return cntSum;
 7     }
 8 
 9     // 回溯每个结点判断从根节点开始往下是否存在等于sum的路径
10     public void traceBack(TreeNode root, int nowSum, int sum){
11         if(root != null){
12             nowSum += root.val;
13             if(sum == nowSum){
14                 cnt++;
15             }
16             traceBack(root.left, nowSum, sum);
17             traceBack(root.right, nowSum, sum);
18         }
19     }
20 
21     // 遍历树的所有结点,对每个结点判断从根节点开始往下是否存在等于sum的路径
22     // 前序递归遍历树节点
23     public void preTraversal(TreeNode root, int sum){
24         if(root != null){
25             cnt = 0;
26             traceBack(root, 0, sum);
27             cntSum += cnt;
28             preTraversal(root.left, sum);
29             preTraversal(root.right, sum);
30         }
31     }
32 }

力扣测试时间为31ms, 空间为39.2MB

另一中稍微简洁一点的写法是:

 1 class Solution {
 2     public int pathSum(TreeNode root, int sum) {
 3        if(root == null){
 4            return 0;
 5        }
 6        // 求出以root为根和为sum的的路径条数
 7         int result = traceBack(root, sum);
 8        // 递归以root.left为根和为sum的的路径条数
 9        int left = pathSum(root.left, sum);
10        // 递归以root.right为根和为sum的的路径条数
11        int right = pathSum(root.right, sum);
12        return result + left + right;
13     }
14 
15     // 回溯每个结点判断从根节点开始往下是否存在等于sum的路径
16     public int traceBack(TreeNode root, int sum){
17         if(root == null){
18             return 0;       // 到达根节点直接返回0
19         }
20         sum -= root.val;
21         int result = sum == 0 ? 1 : 0;
22         // 加上包含左右根节点的路径
23         return result + traceBack(root.left, sum) + traceBack(root.right, sum);
24     }
25 }

力扣测试时间为31ms, 空间为39.7MB(可以看到两种写法的时间都差不多,毕竟都是进行先序遍历,对每个结点再已它为根进行一次前序遍历)

复杂度分析:

时间复杂度:先进行先序遍历,对每个结点再已它为根进行一次前序遍历,所以时间复杂度为O(n^2)

空间复杂度:在最坏情况下,即树变成单链表时,栈的最大为n^2, 所以空间复杂度为O(n^2),

思路二:(不太懂,以后回来看)

可以理解为以当前结点为最终叶子结点向上追溯,路径上的任一结点为根节点到当前结点的路径和为sum的路径个数。 然后以同样的方法以自己的孩子结点为最终叶子结点计算。

 1 class Solution {
 2     public int pathSum(TreeNode root, int sum) {
 3         // 把以当前结点为终止结点的路径和加入到数组中
 4         return helper(root, sum, new int[1000], 0);    // 0表示刚开始的终结结点为0
 5     }
 6     public int helper(TreeNode root, int sum, int[] array, int p){
 7         if(root == null){
 8             return 0;
 9         }
10         // 判断以当前根节点为终止结点是否存在满足要求的路径
11         // 当前结点既是根节点也是终止结点
12         int temp = root.val;
13         int cnt = (temp == sum ? 1 : 0);
14         // 把每个结点都作为开始结点,判断结点到当前结点是否符合要求
15         for(int i = p - 1; i >= 0; i--){
16             temp += array[i];
17             cnt += (temp == sum ? 1 : 0);
18         }
19         array[p] = root.val;
20         // 求出以左右子节点为终止结点,求出符合要求的路径数量
21         int cnt1 = helper(root.left, sum, array, p + 1);
22         int cnt2 = helper(root.right, sum, array, p + 1);
23         return cnt1 + cnt2 + cnt;
24     }
25 
26 }

力扣测试时间为4ms, 空间为39.6MB

复杂度分析:

时间复杂度:虽然变成了单递归,但是复杂度仍然是O(n^2)

空间复杂度:栈的深度最大可大n, 所以空间复杂度为O(n)

思路参考:

https://leetcode-cn.com/problems/path-sum-iii/solution/javajie-fa-shi-jian-100-kong-jian-93-by-xiao-chao-/