Path Sum III | Leetcode 437
Welcome to our deep dive on Path Sum III (Leetcode 437). This is an incredibly elegant problem that merges the Prefix Sum technique from Arrays with a classic DFS tree traversal.
Problem Statement
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:
root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 => Output: 3
Approach: Prefix Sum + DFS Backtracking O(N)
We want to find subarrays (or in this case, a sub-path) that sum to a target. In a flat 1D array, this is solved efficiently in O(N) by tracking a running current_sum and storing its frequencies in a Prefix Sum HashMap.
If current_sum - targetSum exists in our HashMap, it strictly means there is a valid sub-path ending exactly at our current node that sums to targetSum!
We can directly map this Array technique to a Tree! The only difference is that trees branch. We must use DFS Backtracking to strictly ensure we only sum paths on a single vertical branch.
- When we enter a node, we add its value to our running prefix sum.
- We check if
current_sum - targetSumexists in our global HashMap and add that occurrence count to our answer. - We increment the frequency of
current_sumin our HashMap. - We recurse
leftandright. - Backtracking Step: Before we return from the current node's recursion frame, we must remove/decrement
current_sumfrom the HashMap so it doesn't accidentally interfere with an entirely different, parallel branch of the tree!
from collections import defaultdict
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
prefix_sums = defaultdict(int)
# Base case to catch paths that start directly cleanly from the root
prefix_sums[0] = 1
def dfs(node, curr_sum):
if not node:
return 0
curr_sum += node.val
# Count valid sub-paths ending at the current node
count = prefix_sums[curr_sum - targetSum]
# Add current sum to our running map
prefix_sums[curr_sum] += 1
# Recursively search children
count += dfs(node.left, curr_sum)
count += dfs(node.right, curr_sum)
# Backtrack optimally by removing this node's sum!
prefix_sums[curr_sum] -= 1
return count
return dfs(root, 0)
Complexity Analysis
| Metric | Complexity | Explanation |
|---|---|---|
| Time | O(N) | We explicitly cleanly traverse the complete tree absolutely exactly once. HashMap lookups take O(1). |
| Space | O(N) | The HashMap caches at most N identically unique sums directly corresponding exactly to the recursive call stack depth N. |