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