LeetCode 437. Path Sum III【前缀和 / 双重DFS】⭐⭐⭐⭐⭐

15 篇文章 0 订阅

题目描述

在这里插入图片描述

知识点

树、DFS、前缀和

我的傻逼解法

结果

在这里插入图片描述

思路

  1. 使用vector返回所有的子路径…
  2. 啊,我这个方法未免太蠢了吧,我好笨。

代码

//我能想到的只有返回 当前结点的所有路径和vector
class Solution {
public:
    int cnt=0;
    int target;

    int pathSum(TreeNode* root, int sum) {
        target=sum;
        dfs(root);
        return cnt;
    }

    vector<int> dfs(TreeNode* root){
        if(root == NULL){
            return vector<int>();
        }

        vector<int> left = dfs(root->left);
        vector<int> right = dfs(root->right);

        vector<int> cur;
        cur.push_back(root->val);
        if(root->val == target){
            cnt++;
        }
        int newVal;

        for(auto i : left){
            newVal = i+root->val;
            if(newVal == target){
                cnt++;
            }
            cur.push_back(newVal);
        } 

        for(auto i : right){
            newVal = i+root->val;
            if(newVal == target){
                cnt++;
            }
            cur.push_back(newVal);
        } 
        return cur;
    }
};

双重DFS解法

结果

在这里插入图片描述

思路

① 先序递归遍历每个节点
② 以每个节点作为起始节点DFS寻找满足条件的路径

  • 我们知道,每个结点都有可能作为路径的起点,所以就首先遍历每个结点。然后再以结点作为开始,进行暴力dfs(需要传递一个sum,用于保存当前路径和)。
  • 解题的要点就是要想到每个结点都有可能是路径起点,然后dfs遍历所有路径即可。

代码

/**
 * 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 cnt=0;

    //pathSum用于返回root为根节点的子树的所有可能情况。
    int pathSum(TreeNode* root, int sum) {
        if(root==NULL){
            return 0;
        }
        dfs(root,sum);
        pathSum(root->left,sum);
        pathSum(root->right,sum);
        
        return cnt;
    }

    //dfs只返回以当前根节点为路径起点的所有可能情况
    void dfs(TreeNode* root,int sum){
        if(root == NULL){
            return;
        }
        sum -= root->val;
        if(sum == 0){
            cnt++;
        }
        dfs(root->left,sum);
        dfs(root->right,sum);
        return;
    }
};

前缀和解法

结果

思路

这道题用到了一个概念,叫前缀和。就是 到达当前元素的路径上,之前所有元素的和。

前缀和怎么应用呢?

如果两个数的前缀总和是相同的,那么这些节点之间的元素总和为零。进一步扩展相同的想法,如果前缀总和currSum,在节点A和节点B处相差target,则位于节点A和节点B之间的元素之和是target。

因为本题中的路径是一棵树,从根往任一节点的路径上(不走回头路),有且仅有一条路径,因为不存在环。(如果存在环,前缀和就不能用了,需要改造算法)

抵达当前节点(即B节点)后,将前缀和累加,然后查找在前缀和上,有没有前缀和currSum-target的节点(即A节点),存在即表示从A到B有一条路径之和满足条件的情况。结果加上满足前缀和currSum-target的节点的数量。然后递归进入左右子树。

左右子树遍历完成之后,回到当前层,需要把当前节点添加的前缀和去除。避免回溯之后影响上一层。因为思想是前缀和,不属于前缀的,我们就要去掉它。

时间复杂度:每个节点只遍历一次,O(N).

空间复杂度:开辟了一个hashMap,O(N).

作者:burning-summer
链接:https://leetcode-cn.com/problems/path-sum-iii/solution/qian-zhui-he-di-gui-hui-su-by-shi-huo-de-xia-tian/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

代码

class Solution {
public:
    unordered_map<int,int> mp;
    int ans;

    int pathSum(TreeNode* root, int sum) {
        ans=0;
        mp[0]=1;    //这里很重要!
        dfs(root,sum,0);
        return ans;
    }

    void dfs(TreeNode* root,int sum,int prefixSum){
        if(!root){
            return;
        }
        //得到当前前缀和
        int curSum = prefixSum + root->val;
        int remain = curSum - sum;
        if(mp.count(remain) != 0){
            ans += mp[remain];
        }
        mp[curSum]++;
        dfs(root->left,sum,curSum);
        dfs(root->right,sum,curSum);
        mp[curSum]--;
        return;
    }
};

反思

  1. 前缀和让我眼前一亮,看来 前缀和中使用hashMap才是王道 !之前学的数组啥的还只是小case!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值