113 - Path Sum II
Difficulty: Medium | Pattern: Tree DFS + Backtracking | Company tags: Amazon, Microsoft, Bloomberg
Problem Statement
Given the root of a binary tree and an integer targetSum, return all root-to-leaf paths where the sum of the node values equals targetSum. Each path should be returned as a list of the node values.
Example:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
Output: [[5,4,11,2],[5,8,4,5]]
Algorithm Flow
Approach: DFS + Backtracking — O(n²)
Key insight: DFS from root to every leaf, maintaining a current path. When we reach a leaf and the remaining target is 0, copy the path to results. Use backtracking: add node before recursing, remove after.
def pathSum(root, targetSum: int) -> list[list[int]]:
result = []
def dfs(node, remaining, path):
if not node:
return
path.append(node.val)
remaining -= node.val
# Leaf node and sum matches
if not node.left and not node.right and remaining == 0:
result.append(list(path)) # copy the path
else:
dfs(node.left, remaining, path)
dfs(node.right, remaining, path)
path.pop() # backtrack
dfs(root, targetSum, [])
return result
Dry Run
Tree: 5 → [4→[11→[7,2]], 8→[13, 4→[5,1]]], targetSum=22
dfs(5, 22, []): path=[5], remaining=17
dfs(4, 17, [5,4]): remaining=13
dfs(11, 13, [5,4,11]): remaining=2
dfs(7, 2, [5,4,11,7]): leaf, remaining=-5 ≠ 0
dfs(2, 2, [5,4,11,2]): leaf, remaining=0 ✓ → append [5,4,11,2]
dfs(8, 17, [5,8]): remaining=9
dfs(13, 9, [5,8,13]): leaf, remaining=-4 ≠ 0
dfs(4, 9, [5,8,4]): remaining=5
dfs(5, 5, [5,8,4,5]): leaf, remaining=0 ✓ → append [5,8,4,5]
dfs(1, 5, [5,8,4,1]): leaf, remaining=4 ≠ 0
Result: [[5,4,11,2],[5,8,4,5]] ✓
Why list(path) Not path
result.append(path) would append a reference to the same mutable list, which gets modified by backtracking. Always copy: result.append(list(path)) or result.append(path[:]).
Comparison with LC 112
| Problem | Returns |
|---|---|
| 112 - Path Sum | bool (any valid path?) |
| 113 - Path Sum II | list of all valid paths |
Same DFS structure — 113 just collects and copies each valid path.
Complexity
- Time: O(n²) — O(n) nodes visited, O(n) to copy each path in worst case
- Space: O(n) — recursion stack + path array
Key Terms
| Term | Definition |
|---|---|
| Backtracking | Undoing a choice (path.pop()) after exploring it, so the same path list can be reused for sibling branches. |
| Path accumulation | Building up a candidate path incrementally as DFS descends, rather than recomputing it from scratch at each node. |
Shallow copy (list(path)) | Creating a new list with the same elements so future mutations to path don't affect the stored result. |
| Root-to-leaf path | A full path from the root to a leaf node; the unit of output for this problem. |
| DFS + Backtracking | The combined pattern of depth-first traversal with explicit state mutation and restoration, common to path-enumeration problems. |
FAQ
- Can this be solved without extra space? No — you need O(n) for the output paths and O(h) for the recursion stack; this is inherent to enumerating all matching paths.
- What if the input graph (tree) had a cycle? Binary trees are acyclic by definition, so this doesn't apply here; if it did, DFS would need a visited set to avoid infinite recursion.
- Why must we copy
pathinto the result instead of appending it directly?pathis a single mutable list reused across the whole traversal; appending a reference would mean all stored "paths" end up reflecting the final state ofpathafter backtracking pops everything. - What's the difference between this and 112 - Path Sum? 112 only needs to know if any valid path exists (returns bool, can short-circuit); 113 must find and collect every valid path, so it can't stop early and must copy state at each match.
- How would this change if we needed the path with the maximum or minimum sum instead of an exact target? You'd drop the target-matching condition and instead track/compare sums as you go, updating a "best path" variable at each leaf instead of appending every match.
Quick Revision
- Problem: return all root-to-leaf paths whose values sum to
targetSum. - Approach: DFS while maintaining a running
pathlist andremainingsum. - Append
node.valtopathbefore recursing, pop it after (backtracking). - At a leaf with
remaining == 0, copypathinto the result list. - Must use
list(path)orpath[:]— never append the mutablepathreference directly. - Recurse into both children when not at a matching leaf.
- Time: O(n²) worst case due to path copies; Space: O(n) for recursion + path storage.
- Same DFS skeleton as 112 - Path Sum, but collects all paths instead of returning a boolean.
Related Problems
- 112 - Path Sum — same DFS pattern, but only checks if any valid path exists (returns boolean).
- Pattern: DFS + Backtracking with path accumulation, also seen in "Sum Root to Leaf Numbers" and "Binary Tree Paths".