# Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def pathSum(self, root: TreeNode, targetSum: int) -> int:
from collections import defaultdict
def dfs(node, curr_sum):
if not node:
return 0
curr_sum += node.val
res = prefix_sums[curr_sum - targetSum]
prefix_sums[curr_sum] += 1
res += dfs(node.left, curr_sum)
res += dfs(node.right, curr_sum)
prefix_sums[curr_sum] -= 1
return res
prefix_sums = defaultdict(int)
prefix_sums[0] = 1
return dfs(root, 0)
// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
unordered_map<int, int> prefixSums;
prefixSums[0] = 1;
return dfs(root, 0, sum, prefixSums);
}
int dfs(TreeNode* node, int currSum, int target, unordered_map<int, int>& prefixSums) {
if (!node) return 0;
currSum += node->val;
int res = prefixSums[currSum - target];
prefixSums[currSum]++;
res += dfs(node->left, currSum, target, prefixSums);
res += dfs(node->right, currSum, target, prefixSums);
prefixSums[currSum]--;
return res;
}
};
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
class Solution {
public int pathSum(TreeNode root, int sum) {
Map<Integer, Integer> prefixSums = new HashMap<>();
prefixSums.put(0, 1);
return dfs(root, 0, sum, prefixSums);
}
private int dfs(TreeNode node, int currSum, int target, Map<Integer, Integer> prefixSums) {
if (node == null) return 0;
currSum += node.val;
int res = prefixSums.getOrDefault(currSum - target, 0);
prefixSums.put(currSum, prefixSums.getOrDefault(currSum, 0) + 1);
res += dfs(node.left, currSum, target, prefixSums);
res += dfs(node.right, currSum, target, prefixSums);
prefixSums.put(currSum, prefixSums.get(currSum) - 1);
return res;
}
}
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
var pathSum = function(root, targetSum) {
const prefixSums = new Map();
prefixSums.set(0, 1);
function dfs(node, currSum) {
if (!node) return 0;
currSum += node.val;
let res = prefixSums.get(currSum - targetSum) || 0;
prefixSums.set(currSum, (prefixSums.get(currSum) || 0) + 1);
res += dfs(node.left, currSum);
res += dfs(node.right, currSum);
prefixSums.set(currSum, prefixSums.get(currSum) - 1);
return res;
}
return dfs(root, 0);
};
targetSum
. These paths do not need to start at the root or end at a leaf, but they must go downwards (from parent to child nodes). Each node may only be used once per path, and the tree may contain negative values. The goal is to count all such valid paths in the tree.
targetSum
. However, this quickly becomes inefficient as the number of nodes grows, since you'd be recalculating many of the same sums repeatedly.
The key insight is to recognize that the problem is similar to the "subarray sum equals k" problem, but on a tree. Instead of recalculating sums for every path, we can keep track of the cumulative sum from the root to the current node and use a hash map to store how many times each cumulative sum has occurred. This allows us to efficiently determine how many previous paths could be extended to form a path that sums to targetSum
.
prefixSums
) to record how many times a particular cumulative sum has occurred so far.currentSum - targetSum
exists in the map. If it does, it means there is a path ending at the current node whose sum is targetSum
.Consider the following tree and targetSum = 8
:
10 / \ 5 -3 / \ \ 3 2 11 / \ \ 3 -2 1
In total, there are 3 valid paths that sum to 8: [5,3], [5,2,1], and [-3,11].