LeetCode 第 437 题「路径总和 III」
字数 1024 2025-10-26 12:39:58
我来给你讲解 LeetCode 第 437 题「路径总和 III」。虽然它在你的列表中出现过,但根据你的要求,我将详细讲解它的描述和解题过程。
题目描述
给定一个二叉树的根节点 root,和一个整数 targetSum,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:有 3 条路径的和等于 8:
- 5 → 3
- 5 → 2 → 1
- -3 → 11
解题思路
步骤 1:理解路径的灵活性
路径不一定从根开始,也不一定到叶子结束,所以任意节点都可以作为路径的起点或终点。
关键:对于每个节点,我们考虑以它为终点的路径中,有多少条路径的和等于 targetSum。
步骤 2:暴力法(DFS 双重递归)
最直接的方法是:
- 遍历每个节点(第一层递归)。
- 对于每个节点,计算以它为终点的路径和(第二层递归)。
代码框架:
def pathSum(root, targetSum):
if not root:
return 0
# 计算以当前节点为终点的路径数 + 左子树路径数 + 右子树路径数
return dfs(root, targetSum) + pathSum(root.left, targetSum) + pathSum(root.right, targetSum)
def dfs(node, target):
if not node:
return 0
count = 0
if node.val == target:
count += 1
# 继续向左右子树搜索,但目标值变为 target - node.val
count += dfs(node.left, target - node.val)
count += dfs(node.right, target - node.val)
return count
时间复杂度:O(n²),最坏情况每个节点都要遍历其子树。
步骤 3:优化思路(前缀和 + 回溯)
我们可以用 前缀和 来优化:
- 在遍历树时,记录从根节点到当前节点的路径和
currSum。 - 如果存在一个之前的前缀和
prevSum使得currSum - prevSum = targetSum,那么从prevSum对应的节点到当前节点的路径和就是targetSum。
具体步骤:
- 用一个哈希表
prefixSumCount记录路径上每个前缀和出现的次数。 - 递归遍历二叉树,更新当前路径和
currSum。 - 检查
currSum - targetSum是否在哈希表中,如果在,说明存在路径。 - 递归左右子树。
- 回溯时,将当前路径和的前缀计数减一(避免影响其他分支)。
步骤 4:优化后的代码实现
def pathSum(root, targetSum):
from collections import defaultdict
prefixSumCount = defaultdict(int)
prefixSumCount[0] = 1 # 空路径的前缀和为 0
return dfs(root, 0, targetSum, prefixSumCount)
def dfs(node, currSum, target, prefixSumCount):
if not node:
return 0
currSum += node.val
# 查找符合条件的前缀和
count = prefixSumCount[currSum - target]
# 更新哈希表
prefixSumCount[currSum] += 1
# 递归左右子树
count += dfs(node.left, currSum, target, prefixSumCount)
count += dfs(node.right, currSum, target, prefixSumCount)
# 回溯
prefixSumCount[currSum] -= 1
return count
时间复杂度:O(n),每个节点只遍历一次。
空间复杂度:O(n),哈希表和递归栈的空间。
总结
- 暴力法:双重递归,直观但效率低。
- 优化法:利用前缀和将问题转化为“两数之差”问题,通过哈希表快速查找,将时间复杂度降为 O(n)。
如果你对前缀和或回溯部分还有疑问,我可以进一步解释。