[LeetCode] 437. Path Sum III 二叉树的路径和之三

 

Given the root of a binary tree and an integer targetSum, return the number of paths where the sum of the values along the path equals targetSum.

The path does not need to start or end at the root or a leaf, but it must go downwards (i.e., traveling only from parent nodes to child nodes).

 

Example 1:

Input: root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
Output: 3
Explanation: The paths that sum to 8 are shown.

Example 2:

Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
Output: 3

 

Constraints:

  • The number of nodes in the tree is in the range [0, 1000].
  • -109 <= Node.val <= 109
  • -1000 <= targetSum <= 1000

 

这道题让我们求二叉树的路径的和等于一个给定值,说明了这条路径不必要从根节点开始,可以是中间的任意一段,而且二叉树的节点值也是有正有负。那么可以用递归来做,相当于先序遍历二叉树,对于每一个节点都有记录了一条从根节点到当前节点到路径,同时用一个变量 curSum 记录路径节点总和,然后看 curSum 和 sum 是否相等,相等的话结果 res 加1,不等的话继续查看子路径和有没有满足题意的,做法就是每次去掉一个节点,看路径和是否等于给定值,注意最后必须留一个节点,不能全去掉了,因为如果全去掉了,路径之和为0,而如果给定值刚好为0的话就会有问题,整体来说不算一道很难的题,参见代码如下:

 

解法一:

class Solution {
public:
    int pathSum(TreeNode* root, int targetSum) {
        int res = 0;
        vector<TreeNode*> out;
        helper(root, targetSum, 0, out, res);
        return res;
    }
    void helper(TreeNode* node, int targetSum, long curSum, vector<TreeNode*>& out, int& res) {
        if (!node) return;
        curSum += node->val;
        out.push_back(node);
        if (curSum == targetSum) ++res;
        long t = curSum;
        for (int i = 0; i < out.size() - 1; ++i) {
            t -= out[i]->val;
            if (t == targetSum) ++res;
        }
        helper(node->left, targetSum, curSum, out, res);
        helper(node->right, targetSum, curSum, out, res);
        out.pop_back();
    }
};

 

我们还可以对上面的方法进行一些优化,来去掉一些不必要的计算,可以用 HashMap 来建立路径之和跟其个数之间的映射,即路径之和为 curSum 的个数为 m[curSum],这里需要将 m[0] 初始化为1,后面会讲解原因。在递归函数中,首先判空,若为空,直接返回0。然后就是 curSum 加上当前结点值。由于此时 curSum 可能已经大于了目标值 sum,所以用 curSum 减去 sum,并去 HashMap 中查找这个差值的映射值,若映射值大于0的化,说明存在结束点为当前结点且和为 sum 的路径,这就相当于累加和数组快速求某个区域和的原理。当 curSum 等于 sum 的时候,表明从根结点到当前结点正好是一条符合要求的路径,此时 res 应该大于0,这就是为啥要初始化 m[0] 为1的原因,举个例子来说吧,看下面这棵树:

    1
   /
  2
 /
1

假设 sum=3,当遍历根结点1时,curSum 为1,那么 curSum-sum=-2,映射值为0,然后建立映射 m[1]=1。此时遍历结点2,curSum 为3,那么 curSum-sum=0,由于 m[0] 初始化为1,所以 res=1,这是 make sense 的,因为存在和为3的路径。此时再遍历到第二个结点1,curSum 为4,那么 curSum-sum=1,由于之前建立了 m[1]=1 的映射,所以当前的 res 也为1,因为存在以第二个结点1为结尾且和为3的路径,那么递归返回到结点2时候,此时的 res 累加到了2,再返回根结点1时,res 就是2,最终就返回了2,那么可以看出递归函数的意义,某个结点的递归函数的返回值就是该结点上方或下方所有和为 sum 的路径总个数,参见代码如下:

 

解法二:

class Solution {
public:
    int pathSum(TreeNode* root, int targetSum) {
        unordered_map<long, int> m;
        m[0] = 1;
        return helper(root, (long)targetSum, 0, m);
    }
    int helper(TreeNode* node, long targetSum, long curSum, unordered_map<long, int>& m) {
        if (!node) return 0;
        curSum += node->val;
        int res = m[curSum - targetSum];
        ++m[curSum];
        res += helper(node->left, targetSum, curSum, m) + helper(node->right, targetSum, curSum, m);
        --m[curSum];
        return res;
    }
};

 

下面这种方法非常的简洁,用了两个递归函数,其中的一个 sumUp 递归函数是以当前结点为起点,和为 sum 的路径个数,采用了前序遍历,对于每个遍历到的节点进行处理,维护一个变量 pre 来记录之前路径之和,然后 cur 为 pre 加上当前节点值,如果 cur 等于 sum,那么返回结果时要加1,然后对当前节点的左右子节点调用递归函数求解,这是 sumUp 递归函数。而在 pathSum 函数中,我们对当前结点调用 sumUp 函数,加上对左右子结点调用 pathSum 递归函数,三者的返回值相加就是所求,参见代码如下:

 

解法三:

class Solution {
public:
    int pathSum(TreeNode* root, int targetSum) {
        if (!root) return 0;
        return sumUp(root, 0, targetSum) + pathSum(root->left, targetSum) + pathSum(root->right, targetSum);
    }
    int sumUp(TreeNode* node, long pre, int targetSum) {
        if (!node) return 0;
        long cur = pre + node->val;
        return (cur == targetSum) + sumUp(node->left, cur, targetSum) + sumUp(node->right, cur, targetSum);
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/437

 

类似题目:

Binary Tree Maximum Path Sum

Path Sum II

Path Sum

Minimum Path Sum

Path Sum IV

Longest Univalue Path 

 

参考资料:

https://leetcode.com/problems/path-sum-iii/

https://leetcode.com/problems/path-sum-iii/discuss/91889/Simple-Java-DFS

https://leetcode.com/problems/path-sum-iii/discuss/91878/17-ms-O(n)-java-Prefix-sum-method

 

LeetCode All in One 题目讲解汇总(持续更新中...)

posted @ 2016-10-28 12:32  Grandyang  阅读(24380)  评论(19编辑  收藏  举报
Fork me on GitHub