1. 题目
https://leetcode.cn/problems/path-sum-iii/
考察点
这道题的考察点是:
- 二叉树的遍历,如何使用深度优先搜索或广度优先搜索来访问每个节点。
- 前缀和的概念,如何利用前缀和来快速计算路径和,以及如何处理负数的情况。
- 哈希表的使用,如何利用哈希表来存储和查找前缀和,以及如何处理哈希冲突的情况。
- 回溯法的思想,如何在递归遍历时更新和恢复状态,以免影响其他分支的搜索。
2. 解法
解答思路
整体的思路是利用前缀和和哈希表来统计二叉树中路径和等于目标和的路径数。
- 前缀和可以帮助我们快速计算从根节点到任意节点的路径和,
- 哈希表可以帮助我们快速查找是否存在某个前缀和,以及其出现的次数。
- 通过深度优先搜索遍历二叉树,我们可以更新前缀和和哈希表,并检查是否有满足条件的子路径。最后返回路径数作为答案。
什么是前缀和
好的,我可以给你一个二叉树的例子和对应的前缀和。假设二叉树如下:
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
10
/ \
15 7
/ \ \
18 17 18
/ \ \
21 16 18
可以看出,前缀和就是沿着路径累加节点的值,比如从根节点到左子树的右子树的右子树的节点,路径为10 -> 5 -> 2 -> 1,那么前缀和为10 + 5 + 2 + 1 = 18。
代码逻辑
首先,我们需要定义一个全局变量 count 来记录满足条件的路径数,以及一个哈希表 map 来记录前缀和出现的次数。
前缀和是指从根节点到当前节点的路径上的节点值之和。
然后,我们使用深度优先搜索遍历二叉树,对于每个节点,我们更新当前路径和 currSum 为其父节点的路径和加上当前节点的值。
然后,我们在哈希表中查找 currSum - targetSum 的次数,
如果存在,说明存在一条或多条从根节点到当前节点的子路径,其和为 targetSum,我们将这个次数加到 count 上。
接着,我们将 currSum 的次数加一,存入哈希表中,表示当前路径和已经出现过一次。
接下来,我们递归地遍历当前节点的左右子树,
重复上述过程。
当遍历完左右子树后,我们需要回溯,将 currSum 的次数减一,从哈希表中移除,以免影响其他分支的搜索。
最后,我们返回 count 作为答案。
以上代码的逻辑可以分为以下几个步骤:
- 定义一个全局变量 count 来记录满足条件的路径数,以及一个哈希表 map 来记录前缀和出现的次数,初始值为 (0, 1),表示和为0的路径有1条(空路径)。
- 定义一个辅助函数 helper,参数为当前节点,目标和,当前路径和,以及哈希表。
- 在 helper 函数中,如果当前节点为空,直接返回。
- 更新当前路径和为其父节点的路径和加上当前节点的值。
- 在哈希表中查找 currSum - targetSum 的次数,如果存在,将这个次数加到 count 上。
- 将 currSum 的次数加一,存入哈希表中。
- 递归地遍历当前节点的左右子树,重复步骤3-6。
- 回溯时,将 currSum 的次数减一,从哈希表中移除。
- 定义一个主函数 pathSum,参数为根节点和目标和。
- 在主函数中,如果根节点为空,直接返回0。
- 调用 helper 函数来遍历二叉树,传入根节点,目标和,0,以及哈希表。
- 返回 count 作为答案。
具体实现
class Solution { // 定义一个全局变量来记录路径数 int count = 0; public int pathSum(TreeNode root, int targetSum) { // 如果根节点为空,直接返回0 if (root == null) return 0; // 创建一个哈希表来记录前缀和出现的次数,初始值为(0, 1),表示和为0的路径有1条(空路径) HashMap<Integer, Integer> map = new HashMap<>(); map.put(0, 1); // 调用辅助函数来遍历二叉树 helper(root, targetSum, 0, map); // 返回最终的路径数 return count; } // 定义一个辅助函数来遍历二叉树,参数为当前节点,目标和,当前路径和,以及哈希表 private void helper(TreeNode node, int targetSum, int currSum, HashMap<Integer, Integer> map) { // 如果当前节点为空,直接返回 if (node == null) return; // 更新当前路径和 currSum += node.val; // 如果哈希表中存在当前路径和减去目标和的键,说明存在一条或多条子路径和等于目标和,更新路径数 count += map.getOrDefault(currSum - targetSum, 0); // 将当前路径和加入哈希表,并增加其出现次数 map.put(currSum, map.getOrDefault(currSum, 0) + 1); // 递归遍历左右子树 helper(node.left, targetSum, currSum, map); helper(node.right, targetSum, currSum, map); // 回溯时,将当前路径和从哈希表中移除,并减少其出现次数 map.put(currSum, map.get(currSum) - 1); } }