437. Path Sum III 二叉树的路径和三

题目链接
tag:

  • Medium;
  • DFS;
  • 前缀和

question:
  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

方法一:
思路:我们首先想到的解法是穷举所有的可能,我们访问每一个节点 node,检测以 node 为起始节点且向下延深的路径有多少种。我们递归遍历每一个节点的所有可能的路径,然后将这些路径数目加起来即为返回结果。

  • 我们首先定义 pathSum(p, val) 表示以节点 p 为起点向下且满足路径总和为 val 的路径数目。我们对二叉树上每个节点 p 求出 pathSum(p, targetSum),然后对这些路径数目求和即为返回结果。
  • 我们对节点 p 求 pathSum(p, targetSum) 时,以当前节点 p 为目标路径的起点递归向下进行搜索。假设当前的节点 p 的值为 val,我们对左子树和右子树进行递归搜索,对节点 p 的左孩子节点 p_l 求出 pathSum(p->left ,targetSum−val),以及对右孩子节点 p_r 求出 pathSum(p->right, targetSum−val)。节点 p 的 pathSum(p, targetSum−val) 即等于pathSum(p_r, targetSum−val) 与 pathSum(p_r,targetSum−val) 之和,同时我们还需要判断一下当前节点 p 的值是否刚好等于 targetSum。
  • 我们采用递归遍历二叉树的每个节点 p,对节点 p 求 pathSum(p, val),然后将每个节点所有求的值进行相加求和返回。代码如下:
/**
 * 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 pathSum(TreeNode* root, int targetSum) {
        // DFS
        // 时间:O(N^2) 空间O(N)
        if (root == nullptr) return 0;
        int res = pathSumDFS(root, targetSum);
        res += pathSum(root->left, targetSum);
        res += pathSum(root->right, targetSum);
        return res;
    }

    int pathSumDFS (TreeNode* root, int targetSum) {
        if (root == nullptr) return 0;
        int res = 0;
        if (root->val == targetSum) ++res;
        res += pathSumDFS(root->left, targetSum - root->val);
        res += pathSumDFS(root->right, targetSum - root->val);
        return res;
    }
};

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

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

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

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

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

/**
 * 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:
    unordered_map prefix_sum;
    int pathSumDFS(TreeNode* root, int curr, int targetSum) {
        // DFS
        if (root == nullptr) return 0;
        int res = 0;
        
        curr += root->val;
        if (prefix_sum.count(curr - targetSum)) res += prefix_sum[curr - targetSum];
        prefix_sum[curr]++;
        res += pathSumDFS(root->left, curr, targetSum);
        res += pathSumDFS(root->right, curr, targetSum);
        prefix_sum[curr]--;
        return res;
    }

    int pathSum(TreeNode* root, int targetSum) {
        // 前缀和 + DFS
        // 时间:O(N) 空间:O(N)
        prefix_sum[0] = 1;
        return pathSumDFS(root, 0, targetSum);
    }
};

类似题目:

  • 112. Path Sum 二叉树的路径和
  • 113. Path Sum II 路径和 ||

你可能感兴趣的:(437. Path Sum III 二叉树的路径和三)