437. 路径总和 III ●●

437. 路径总和 III ●●

描述

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

示例

在这里插入图片描述
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

题解

1. 分治 + 深度优先搜索 DFS

对每一个节点都求一次以该节点为路径起点的路径数量。
节点数值有正有负,所以递归到叶子节点结束。

  • 时间复杂度: O ( N 2 ) O(N^2) O(N2),其中 N 为该二叉树节点的个数。对于每一个节点,求以该节点为起点的路径数目时,则需要遍历以该节点为根节点的子树的所有节点,因此求该路径所花费的最大时间为 O(N),我们会对每个节点都求一次以该节点为起点的路径数目,因此时间复杂度为 O ( N 2 ) O(N^{2}) O(N2)
  • 空间复杂度:O(N),考虑到递归需要在栈上开辟空间。
class Solution {
public:
    int rootSum(TreeNode* node, long long target){ 
        if(!node) return 0;
        int count = 0;
        if(target == node->val) ++count;    // 当前节点满足条件,计数+1,并继续往下查找
        count += rootSum(node->left, target-node->val) + rootSum(node->right, target-node->val);
        return count;
        // return (target == node->val) 
        //        + rootSum(node->left, target-node->val) 
        //        + rootSum(node->right, target-node->val);
    }  
    
    int pathSum(TreeNode* root, int targetSum) {
        if(!root) return 0;
        int ans = 0;                            // 分治
        ans += rootSum(root, targetSum);        // 以root为起点,查找以该节点为路径开头,且和为targetSum的路径数量
        ans += pathSum(root->left, targetSum);  // 以root->left为根的子树中,和为targetSum的路径数量
        ans += pathSum(root->right, targetSum); // 以root->right为根的子树中,查找和为targetSum的路径数量
        return ans;
    }
};
2. 前缀和

解法一中应该存在许多重复计算。

我们定义节点的前缀和为:由根结点到当前结点的路径上所有节点的和。

我们利用先序遍历二叉树,记录下根节点 root 到当前节点 node 的路径上除当前节点以外所有节点的前缀和,在已保存的路径前缀和中查找是否存在前缀和刚好等于当前节点到根节点的前缀和 curr 减去 targetSum,如果存在,表示存在中间节点到 node 节点(结尾)的路径和为 targetSum,且这些路径的数量由 preCnt 前缀和哈希表给出。

注意:

  1. 对于空路径我们也需要保存预先处理一下,此时因为空路径不经过任何节点,因此它的前缀和为 0,即preCnt[0] = 1;
  2. 退出当前节点的路径时,需要注意更新哈希表中对应前缀和的数量。
  3. 利用深度优先搜索,计算以每个节点为结尾时的符合条件的路径之和。
  • 时间复杂度:O(N),其中 N 为二叉树中节点的个数。利用前缀和只需遍历一次二叉树即可。
  • 空间复杂度:O(N)。
class Solution {
public:
    unordered_map<long long, int> preCnt;       // <路径和, 个数>
    
    int dfs(TreeNode* node, long long curr, int target){
        if(node == nullptr) return 0;
        int cnt = 0;
        curr += node->val;                      // curr 为根节点到 node 节点的路径和,存在curr-target表示存在中间节点到node节点的路径和为target
        if(preCnt.count(curr-target)){          // 以node节点结尾的路径和为target的数量
            cnt += preCnt[curr-target];
        }
        
        ++preCnt[curr];                         // 哈希记录路径和
        cnt += dfs(node->left, curr, target);   // 以node->left结尾的路径和为target的数量
        cnt += dfs(node->right, curr, target);  // 以node->eight结尾的路径和为target的数量
        --preCnt[curr];                         // 退出当前节点,回溯
        return cnt;
    }
    
    int pathSum(TreeNode* root, int targetSum) {
        preCnt[0] = 1;                      // 有一条空路径,和为0
        return dfs(root, 0, targetSum);
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值