437. Path Sum III
At a Glance
- Topic: trees
- Pattern: Prefix sum on tree + Tree DFS
- Difficulty: Medium
- Companies: Amazon, Google, Microsoft, Meta, Apple
- Frequency: High
- LeetCode: 437
Problem (one-liner)
Given the root of a binary tree and an integer targetSum, return the number of downward paths (node→…→descendant, length ≥ 1) whose values sum to targetSum. Paths do not need to start at the root or end at a leaf.
Recognition Cues
- Count paths with sum constraint on trees (vertical chains)
- Same “prefix sum ↔ complement” idea as arrays, but prefix is root-to-current on current DFS branch
- Must reset frequency map when unwinding (different branches share prefixes differently)
Diagram
At-a-glance flow (replace with problem-specific Mermaid as you refine this note). camelCase node IDs; no spaces in IDs.
Approaches
- Brute force — try every ancestor as start, DFS down —
O(n²)on skew trees. - Better — prefix hashmap per root-to-node path —
O(n)time average if map opsO(1). - Optimal —
O(n)time /O(h)stack + map — DFS from root; map counts prefix sums; on exit decrement count for current prefix.
Optimal Solution
Go
package main
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func pathSum(root *TreeNode, targetSum int) int {
counts := map[int]int{0: 1}
var walk func(*TreeNode, int) int
walk = func(node *TreeNode, prefix int) int {
if node == nil {
return 0
}
prefix += node.Val
ways := counts[prefix-targetSum]
counts[prefix]++
ways += walk(node.Left, prefix) + walk(node.Right, prefix)
counts[prefix]--
return ways
}
return walk(root, 0)
}JavaScript
class TreeNode {
constructor(val = 0, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function pathSum(root, targetSum) {
const counts = new Map([[0, 1]]);
function walk(node, prefix) {
if (node === null) {
return 0;
}
prefix += node.val;
let ways = counts.get(prefix - targetSum);
if (ways === undefined) {
ways = 0;
}
counts.set(prefix, (counts.get(prefix) ?? 0) + 1);
ways += walk(node.left, prefix) + walk(node.right, prefix);
counts.set(prefix, counts.get(prefix) - 1);
return ways;
}
return walk(root, 0);
}Walkthrough
Let targetSum = 8. Suppose along one downward walk prefixes hit {0:1, 5:1, 8:1} when prefix becomes 8: counts[8-8]=counts[0]=1 records one path ending at the current node. If a child later also reaches prefix=8, you may count another path that started below the original root of the subcall—map updates on the recursion stack keep branch-local history.
| event | prefix | add to answer | map note |
|---|---|---|---|
| start | 0 (virtual) | {0:1} | |
visit node 5 | 5 | counts[5-8] | increment 5 |
| return / unwind | decrement 5 so parallel branches stay independent |
Invariant: counts only tracks prefix frequencies on the active path from the global root; post-order cleanup keeps cousin subtrees from sharing prefix state incorrectly.
Edge Cases
- Negative values — cannot prune by monotonicity; map approach still correct
- Zero target — multiple overlaps possible; map counts handle multiplicity
targetSumequals single node value — path length 1
Pitfalls
- Forgetting
counts[0]=1baseline (empty prefix before root) - Not undoing
counts[prefix]++after processing node (breaks sibling paths)
Similar Problems
- 113. Path Sum II — enumerate paths with exact root-to-leaf constraint.
- 1448. Count Good Nodes in Binary Tree — another path-value aggregation down from the root.
- 543. Diameter of Binary Tree — different path statistic, still “paths in tree” practice.
Variants
- Path must start at root or end at leaf only — tighter constraints, often simpler DFS.
- Path Sum I style existence check — boolean DFS without counting multiplicity.
Mind-Map Tags
#trees #prefix-sum #hashmap #backtrack-unwind #path-count
Last updated on
Spotted something unclear or wrong on this page?