Algorithm

Binary Tree Maximum Path Sum

Trees Pattern

Binary Tree Maximum Path Sum

A path is any sequence of nodes where each consecutive pair is joined by an edge, and no node appears twice. It does not have to pass through the root, and it does not have to start or end at a leaf. Given the root of a binary tree, return the largest possible sum of the values along such a path. The path must contain at least one node, so when every value is negative the answer is the single largest (least negative) value, never 0.

CONSTRAINTS
  • The number of nodes in the tree is in the range [1, 3 × 10⁴]
  • -1000 <= Node.val <= 1000
  • The path must be non-empty — the empty path is not a valid answer
  • The path need not include the root or any leaf
EXAMPLE 1
Input: root = [1,2,3]
Output: 6
The path 2 → 1 → 3 collects all three values. It bends at the root, which is allowed because no node is repeated.
EXAMPLE 2
Input: root = [-10,9,20,null,null,15,7]
Output: 42
15 + 20 + 7 = 42, a path that bends at 20 and never touches the root. Routing through the root would force -10 into the total and also give up one of 15 or 7.
EXAMPLE 3
Input: root = [-3]
Output: -3
A path must contain at least one node, so the least-bad option is the single node itself. Returning 0 by 'taking nothing' is not permitted.
EXAMPLE 4
Input: root = [2,-1,-2]
Output: 2
Both children would lower the total, so the best path is the root on its own. A path is free to stop anywhere — it is never obliged to descend.
Must the path touch the root, or end at leaves?
Neither. It can be any connected, non-repeating route, including a single node in the middle of the tree.
What if every value is negative?
You still return the largest single value, because the path cannot be empty. Any solution that starts a maximum at 0 gets this case wrong.
Can a path branch at more than one node?
No. It changes direction at most once — going up and then down again would revisit the node you turned at. That single-bend restriction is what makes the problem solvable in one pass.
Is the answer the path itself or just the sum?
Just the sum. Reconstructing the actual path is a reasonable follow-up and needs you to remember where each best value came from.

A path is any connected route through the tree with no node repeated, and we want the one whose values add up to the most. It can start and stop anywhere — no need to touch the root. The twist that makes it interesting: values can be negative, so a longer path isn't always richer. Sometimes you stop.

Here the best path is 15 – 20 – 7, totalling 42. Notice it bends at 20 and never reaches the root:

text
       -10
       /  \
      9    20        best path: 15 + 20 + 7 = 42
          /  \
        15    7
Each node holds two different numbers

Stand on a node after its children have reported their best downward runs. You now care about two things — and confusing them is the whole bug. Look at node 20:

text
        20
       /  \
     15    7

  (A) path that BENDS at 20:   15 + 20 + 7   = 42   <- uses BOTH arms, finished
  (B) run 20 offers UPWARD:    20 + max(15,7) = 35   <- uses ONE arm, can extend

(A) is a finished path: it already spent both of 20's arms, so it can't climb any higher — it just competes for the best-so-far. (B) is what 20 hands to its parent: a path continuing up through 20 can only enter one arm, or it forks and stops being a path. So the rule is simple: record (A) on the side, return (B) upward.

Skip arms that lose you points

An arm can come back negative. You're never forced to walk into it, so clamp each arm at 0 — take it only if it helps:

text
        2
       / \
     -1   -2      both arms negative -> drop both -> best path is just 2
python
best = float('-inf')

def gain(node):
    nonlocal best
    if node is None:
        return 0
    left  = max(0, gain(node.left))            # take the arm only if it helps
    right = max(0, gain(node.right))
    best = max(best, node.val + left + right)   # (A) path bends here
    return node.val + max(left, right)          # (B) run offered upward

gain(root)
return best

Two traps the pictures hide:
- node.val itself is never clamped — only its arms are. On an all-negative tree like a lone -3, both arms clamp to 0 and the best is -3 + 0 + 0 = -3. Start best at negative infinity, not 0, or you'd claim the empty path (sum 0), which isn't allowed.
- (A) adds both arms, (B) keeps the better one — same reason as the picture: a path bending here uses both directions at once; a path continuing upward may leave on only one.

Watch it run

Tree above, root -10:
- Leaf 15 → arms 0, 0 → candidate 15 (best = 15); offers up 15.
- Leaf 7 → candidate 7; offers up 7.
- Node 20 → arms 15 and 7 → candidate 20 + 15 + 7 = 42 (new best!); offers up 20 + max(15, 7) = 35.
- Leaf 9 → candidate 9; offers up 9.
- Root -10 → arms max(0,9) = 9 and max(0,35) = 35 → candidate -10 + 9 + 35 = 34 (loses to 42); offers up -10 + 35 = 25 (nobody reads it).
- Answer 42 — recorded three steps earlier, deep in the tree.

Because every node drops its own (A) candidate into best, the winning path can live anywhere — no special case needed. O(N) time, O(H) stack.

Interactive Strategy Visualization

Optimal Path Explorer

Max Gain and Peak Analysis
10920157
Current Peak Sum0
Global Maximum0
Algorithm Strategy

Starting Post-order traversal. Nodes return 'Gain' (Node + Max Child).

Global High Score
0
Complexity Note
Gain = Node + Math.max(0, LeftGain, RightGain).

Strategy

Focus on the recursive nature of trees: solve for subtrees and combine results at the root.

"Divide and Conquer: Subproblem → Recurrence → Result"

O(N × H) Recompute Downward Runs Per Node
O(N) Time · O(H) Space Fused Pass With Clamped Gains