面试题34-二叉树中和为某一值的路径

题目:

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。

在这里插入图片描述

方法一:深度优先搜索

注意到本题的要求是,找到所有满足从「根节点」到某个「叶子节点」经过的路径上的节点之和等于目标和的路径。核心思想是对树进行一次遍历,在遍历时记录从根节点到当前节点的路径和,以防止重复计算。

我们可以采用深度优先搜索的方式,枚举每一条从根节点到叶子节点的路径。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。

class Solution {
public:
    vector<vector<int>>res;
    vector<int>level;
    vector<vector<int>> pathSum(TreeNode* root, int target) {
        if(root==nullptr)
            return vector<vector<int>>();
        int Sum=0;
        pathSum(root,target,Sum);
        return res;
    }
    void pathSum(TreeNode* root, int target,int Sum) {
        Sum=Sum+root->val;
        level.push_back(root->val);
        //如果是叶节点,并且路径上节点值的和等于目标值
        //放入动态数组中
        bool isLeaf=root->left==nullptr&&root->right==nullptr;
        if(isLeaf&&Sum==target)
            res.push_back(level);
        //如果不是叶节点,则遍历它的子节点
        if(root->left)
            pathSum(root->left,target,Sum);
        if(root->right)
            pathSum(root->right,target,Sum);
        //在返回父节点之前,在路径上删除当前节点
        level.pop_back();
    }
};
  1. 时间复杂度:O(N^2),其中 N是树的节点数。在最坏情况下,树的上半部分为链状,下半部分为完全二叉树,并且从根节点到每一个叶子节点的路径都符合题目要求。此时,路径的数目为 O(N),并且每一条路径的节点个数也为 O(N),因此要将这些路径全部添加进答案中,时间复杂度为 O(N^2)。注:不太理解,DFS时间复杂度不应该是O(N)吗?
  2. 空间复杂度:O(N),其中 N 是树的节点数。空间复杂度主要取决于栈空间的开销,栈中的元素个数不会超过树的节点数。

437-路径总和三

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

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

方法一:深度优先搜索

参考上一题的解题思路,遍历树的每个节点,ans表示符合条件的路径总数。以当前节点为根节点,计算路径和,若路径和=targetSum,ans加1。

class Solution {
public:
int ans=0;
    int pathSum(TreeNode* root, int targetSum) {
        if(!root)
            return 0;
        rootSum(root,targetSum);
        pathSum(root->left,targetSum);
        pathSum(root->right,targetSum);
        return ans;
    }
     void rootSum(TreeNode* root, int target) {
        if(!root)
            return;
        if(root->val==target)
            ans++;
       
        //if(root->left)
            rootSum(root->left,target-root->val);
        //if(root->right)
            rootSum(root->right,target-root->val);   
    }
};
  1. 时间复杂度:O(N^2),其中 N 为该二叉树节点的个数。对于每一个节点,求以该节点为起点的路径数目时,则需要遍历以该节点为根节点的子树的所有节点,因此求该路径所花费的最大时间为 O(N),我们会对每个节点都求一次以该节点为起点的路径数目,因此时间复杂度为 O(N^2 )。
  2. 空间复杂度:O(N)

方法二:前缀和

我们仔细思考一下,解法一中应该存在许多重复计算。我们定义节点的前缀和为:由根结点到当前结点的路径上所有节点的和。我们利用先序遍历二叉树,记录下根节点root 到当前节点 p 的路径上除当前节点以外所有节点的前缀和,在已保存的路径前缀和中查找是否存在前缀和刚好等于当前节点到根节点的前缀和 currcurr 减去targetSum。

对于空路径我们也需要保存预先处理一下,此时因为空路径不经过任何节点,因此它的前缀和为 0。

假设根节点为 root,我们当前刚好访问节点node,则此时从根节点 root 到节点 node 的路径(无重复节点)刚好为 root→p1 →p 2 →…→pk​→node,此时我们可以已经保存了节点 p1, p2, p3,…,pk 的前缀和,并且计算出了节点 node 的前缀和。

假设当前从根节点 root 到节点node 的前缀和为curr,则此时我们在已保存的前缀和查找是否存在前缀和刚好等于 curr−targetSum。假设从根节点 root 到节点node 的路径中存在节点pi到根节点root 的前缀和为 curr−targetSum,则节点 pi+1到node 的路径上所有节点的和一定为 targetSum。

我们利用深度搜索遍历树,当我们退出当前节点时,我们需要及时更新已经保存的前缀和。

class Solution {
public:
    unordered_map<long long, int> prefix;

    int dfs(TreeNode *root, long long curr, int targetSum) {
        if (!root) {
            return 0;
        }

        int ret = 0;
        curr += root->val;//累加前缀和
        if (prefix.count(curr - targetSum)) {
            ret = prefix[curr - targetSum];
        }

        prefix[curr]++;//加入新的前缀和
        ret += dfs(root->left, curr, targetSum);
        ret += dfs(root->right, curr, targetSum);
        prefix[curr]--;//回退

        return ret;
    }

    int pathSum(TreeNode* root, int targetSum) {
        prefix[0] = 1;
        return dfs(root, 0, targetSum);
    }
};
  1. 时间复杂度:O(N),其中 N 为二叉树中节点的个数。利用前缀和只需遍历一次二叉树即可。
  2. 空间复杂度:O(N)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值