leetcode:437. 路径总和 III

题目

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

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

示例 1:
[中等] 437. 路径总和 III - 图1

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

示例 2:

  1. 输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
  2. 输出:3

解答 & 代码

遇到“区间和”的问题就可以用到“前缀和”来解题
思路同:
[中等] 560. 和为 K 的子数组
注意:记得哈希表初始化存入 (0, 0),代表有一条空路径,路径和为 0

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. private:
  14. int pathCnt = 0; // 二叉树中和为 targetSum 的路径数目(起点不限于根节点)
  15. int prePathSum = 0; // 从根节点到当前节点的路径和(即前缀和)
  16. // 哈希表,key = 前缀和,val = 对应的路径数目。前缀和指的是从根节点到某个节点的路径和
  17. unordered_map<int, int> preSumCntMap;
  18. // 深度优先遍历
  19. void dfs(TreeNode* root, int targetSum)
  20. {
  21. if(root == nullptr)
  22. return;
  23. // 前序位置:
  24. prePathSum += root->val; // 更新根节点到当前节点的路径和
  25. // 在哈希表中查找,从根节点开始路径和/前缀和为 prePathSum - targetSum 的路径数目
  26. // 就是路径和为 targetSum 的路径条数(起点不限于根节点),更新 pathCnt
  27. if(preSumCntMap.find(prePathSum - targetSum) != preSumCntMap.end())
  28. pathCnt += preSumCntMap[prePathSum - targetSum];
  29. // 在哈希表中更新 从根节点开始、路径和为 prePathSum 的路径数目
  30. if(preSumCntMap.find(prePathSum) == preSumCntMap.end())
  31. preSumCntMap[prePathSum] = 1;
  32. else
  33. preSumCntMap[prePathSum] += 1;
  34. // 递归处理左、右子树
  35. dfs(root->left, targetSum);
  36. dfs(root->right, targetSum);
  37. // 后序位置:要退出当前节点了,需撤销当前节点对 prePathSum 及 哈希表的改动
  38. preSumCntMap[prePathSum] -= 1;
  39. if(preSumCntMap[prePathSum] == 0)
  40. preSumCntMap.erase(prePathSum);
  41. prePathSum -= root->val;
  42. }
  43. public:
  44. int pathSum(TreeNode* root, int targetSum) {
  45. // 初始时在哈希表中存入 <0, 1>,代表空路径,不含节点(从根节点开始、路径和为 0)
  46. preSumCntMap[0] = 1;
  47. dfs(root, targetSum);
  48. return pathCnt;
  49. }
  50. };

复杂度分析:设二叉树节点数 N

  • 时间复杂度 O(N)
  • 空间复杂度 O(log N):栈空间取决于递归深度,即二叉树的高度

执行结果:

  1. 执行结果:通过
  2. 执行用时:16 ms, 在所有 C++ 提交中击败了 76.32% 的用户
  3. 内存消耗:19.1 MB, 在所有 C++ 提交中击败了17.60% 的用户

更新:leetcode 增加了测试用例,路径和可能超过 int 范围,因此需要修改为 long 类型
另:unordered_map 对于不存在的 key,查找并使用时会自动赋值 value 为 0,因此上述代码其实可以简化

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. private:
  14. int result;
  15. unordered_map<long, int> preSumCntMap;
  16. void backTrace(TreeNode* root, long pathSum, int targetSum)
  17. {
  18. if(root == nullptr)
  19. return;
  20. // 选择
  21. pathSum += root->val;
  22. result += preSumCntMap[pathSum - targetSum];
  23. ++preSumCntMap[pathSum];
  24. // 递归回溯
  25. backTrace(root->left, pathSum, targetSum);
  26. backTrace(root->right, pathSum, targetSum);
  27. // 撤销选择
  28. --preSumCntMap[pathSum];
  29. pathSum -= root->val;
  30. }
  31. public:
  32. int pathSum(TreeNode* root, int targetSum) {
  33. result = 0;
  34. preSumCntMap[0] = 1; // 注意初始存入空路径!即存在一条路径和为 0
  35. backTrace(root, 0, targetSum);
  36. return result;
  37. }
  38. };