Leetcode.0437 | 路径总和

题目

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

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

示例

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

使用双重递归解决问题

双重递归的主要应用于需要对每个节点进行相似的计算的情景,第一个递归用来遍历这些节点,这二个递归用来对这些节点进行计算,进行深度优先搜索。

首先针对二叉树进行先序遍历,访问每一个节点n。在先序遍历的过程中,以遍历到的每个子树的根节点n作为起始点,将子树看成一张图,进行深度优先搜索,寻找满足条件的路径数量。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int res = 0;
    int pathSum(TreeNode* root, int targetSum) {
        if(root == nullptr){
            return 0;
        }
        cal(root,targetSum); //检查以当前根节点为搜索起始点的图,有多少种满足条件的路径
        pathSum(root->left,targetSum); //检查以左孩子为搜索起始点的图,有多少种满足条件的路径
        pathSum(root->right,targetSum);//检查以右孩子为搜索起始点的图,有多少种满足条件的路径
        return res;
    }
    void cal(TreeNode* start,int targetSum){ //以start为图的起始点,进行深度优先搜索
        if(start == nullptr) return;
        if(targetSum - start->val == 0) res ++; //减去本节点的值以后为0,则说明本节点是一条路径的终点。
        cal(start->left,targetSum - start->val);
        cal(start->right,targetSum - start->val);
    }
};

更优的解法:前缀和

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

利用深度搜索对二叉树进行先序遍历,记录下根节点 root 到当前节点 p 的路径上,除当前节点p以外所有节点的前缀和。同时,我们也可以计算出当前节点p的前缀和。使用hash表来存储每一种出现的前缀和对应的节点数。(map<前缀和x,前缀和为x的节点数>)

在遍历过程中,假设我们当前访问到的节点为p,从根节点到当前节点p的前缀和为curr,这时候就从map中查找前面是否存在前缀和刚好等于curr-targetSum。假设从根节点root到节点p的路径上,存在一个节点t,root到t的前缀和为curr-targetSum,则表明发现一条路径t到p,路径和为targetSum。

假设前缀和为curr-targetSum的节点数有res个,则说明t到p路径和为targetSum的节点有res个。res再加上左右子树中满足条件的节点个数,即为最后的答案。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    map<int, int> count; 
    int pathSum(TreeNode* root, int sum) {
        count[0] = 1; //先设定前缀和为0的节点有1个
        return helper(root, sum, 0);
    }
    int helper(TreeNode* root, int sum, int prefix_sum) {
        if (!root) return 0;
        int res = 0;
        prefix_sum += root->val; //计算当前节点的前缀和
        res += count[prefix_sum - sum]; //前缀和为curr-targetSum的节点数
        count[prefix_sum]++;
        res += helper(root->left, sum, prefix_sum) + helper(root->right, sum, prefix_sum);
        count[prefix_sum]--;
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值