Binary Tree Maximum Path Sum: The Hard Version of Diameter
If you interview at Meta, Amazon, Google, expect some version of "Binary Tree Maximum Path Sum". It is rated Hard, and it falls squarely into the Tree DFS pattern — pattern-first preparation beats grinding random problems every time. Part 11 of 11 in the Tree DFS arc.
The Problem
A path is any node sequence connected by edges, appearing at most once each, no root requirement. Return the maximum sum over all paths of at least one node. Values can be negative — which is what promotes Diameter's shape into a hard problem.
Input: root = [-10, 9, 20, None, None, 15, 7]
Output: 42
The path 15 -> 20 -> 7. For root = [-3] the answer is -3.
Recognizing the Tree DFS Pattern
Depth-first search commits to one branch before trying siblings — recursion (or an explicit stack) whose call structure mirrors the tree itself. Its natural questions are about paths (root to leaf), subtree properties (computed bottom-up from children), and exhaustive exploration (islands, connected regions). The recurring design decision is what each recursive call returns versus what it accumulates in shared state.
Same two-quantity shape as Diameter: the value extendable upward (node plus its better arm) differs from the local candidate (node plus both arms). Negativity adds the clamp — an arm that sums negative is worth zero, i.e., excluded — and the all-negative case forbids initializing the best at zero.
The Approach
Post-order helper gain(node): the best downward path starting at node, clamped at zero via max(gain, 0) on each child. At each node, update the global best with node.val + left_gain + right_gain — the path bending here. Return node.val + max(left_gain, right_gain) upward.
Initialize the best to negative infinity so a tree of all negatives returns its largest single node. The clamp and the initialization are the two marks between a pass and a fail on this question.
Python Solution
class TreeNode:
def __init__(self, val: int = 0, left: "TreeNode | None" = None,
right: "TreeNode | None" = None):
self.val = val
self.left = left
self.right = right
def max_path_sum(root: TreeNode) -> int:
"""Maximum sum over all any-to-any paths."""
best = float("-inf")
def gain(node: TreeNode | None) -> int:
nonlocal best
if node is None:
return 0
left = max(gain(node.left), 0) # negative arms are dropped
right = max(gain(node.right), 0)
best = max(best, node.val + left + right)
return node.val + max(left, right)
gain(root)
return int(best)
Complexity
- Time: O(n) — one post-order pass
- Space: O(h) — recursion stack
Interview Tips and Follow-Ups
- Walk the all-negative tree aloud ([-2, -1] returning -1) — it simultaneously tests the clamp and the initialization, and interviewers reach for it first.
- Contrast with Diameter explicitly: same skeleton, plus value weights, plus the zero-clamp. Presenting it as an upgrade shows the pattern lens working.
- Returning the actual path (not just the sum) requires tracking the argmax structure through the recursion — sketch how before it is asked.
If this clicked, continue the Tree DFS arc in the Technical Interview category. One hundred questions, nine patterns, all in Python.
Keep reading
Diameter of a Binary Tree: Answers That Bypass the Root
Return one thing, track another — the two-quantity DFS shape. Python solution and complexity analysis for the DFS interview pattern.
Invert Binary Tree: Swap on the Way Down
The famous swap-children recursion — and its iterative twin. Python solution and complexity analysis for the DFS interview pattern.
Lowest Common Ancestor: The Bubble Up Argument
Return what you found — the split node emerges from the recursion. Python solution and complexity analysis for the DFS interview pattern.
Newsletter
New posts, straight to your inbox
One email per post. No spam, no tracking pixels, unsubscribe anytime.
Comments
- No comments yet. Be the first.