LeetCode ยท #124

Binary Tree Maximum Path Sum Solution

Given a binary tree, find the maximum path sum. A path can start and end at any node โ€” it doesn't need to pass through the root.

01
Section One ยท Problem

Problem Statement

๐Ÿท๏ธ

Difficulty

Hard

๐Ÿ”—

LeetCode

Problem #124

๐Ÿ—๏ธ

Pattern

Post-order DFS โ€” single-path vs split-path

A path in a binary tree is a sequence of nodes where each pair of adjacent nodes has an edge. A path does not need to pass through the root. The path sum is the sum of node values in the path. Return the maximum path sum of any non-empty path.

Example
Input: -10 / \ 9 20 / \ 15 7 Output: 42 // Path: 15 โ†’ 20 โ†’ 7 = 42
Constraints
โ€ข 1 โ‰ค number of nodes โ‰ค 3 ร— 10โด โ€ข -1000 โ‰ค Node.val โ‰ค 1000
02
Section Two ยท Approach 1

Brute Force โ€” O(nยฒ)

For every pair of nodes, compute the path sum between them. O(nยฒ) pairs, each requiring O(n) traversal. Far too slow.

03
Section Three ยท Approach 2

Post-Order DFS โ€” O(n)

At each node, compute the max sum of a path that goes through this node โ€” possibly using both children (split path). But return to the parent only the best single-branch path (can't split upward). Track a global maximum.

๐Ÿ’ก Mental model: Each node asks its children: "What's the best single-arm path you can offer me?" It then considers three options: (1) just itself, (2) itself + left arm, (3) itself + right arm, (4) itself + both arms (split โ€” can't go upward). Options 1-3 are reportable to the parent. Option 4 is only used to update the global max.
Algorithm
  • Recursive function returns the max single-path sum starting from this node going downward.
  • At each node:
  • leftGain = max(0, dfs(left)) โ€” take left branch only if positive.
  • rightGain = max(0, dfs(right)) โ€” same for right.
  • splitPath = node.val + leftGain + rightGain โ€” path through this node using both sides.
  • Update globalMax = max(globalMax, splitPath).
  • Return: node.val + max(leftGain, rightGain) โ€” best single-arm to offer parent.
๐ŸŽฏ Why max(0, ...)?:
  • If a subtree's best path is negative, don't include it โ€” it only hurts the sum.
  • Setting gain to 0 effectively "prunes" that branch. This handles negative values naturally.
Key distinction โ€” split vs single:
  • The split path (left + node + right) can be the answer but cannot be extended upward (you can't go left-node-right-parent โ€” that's not a valid path).
  • So we only return the best single branch to the parent while updating the global max with the split option.
04
Section Four ยท Trace

Visual Walkthrough

Post-order DFS โ€” tree [-10, 9, 20, null, null, 15, 7]
TREE STRUCTURE -10 9 20 15 7 โ† best path: 15โ†’20โ†’7 POST-ORDER TRACE (leaves first, root last) โ‘  Node 9 Leaf โ†’ leftGain=0, rightGain=0 split=9+0+0 = 9 return 9 gMax=9 No children โ€” leaf contributes itself. โ‘ก Node 15 Leaf โ†’ leftGain=0, rightGain=0 split=15+0+0 = 15 return 15 gMax=15 โ‘ข Node 7 Leaf โ†’ leftGain=0, rightGain=0 split=7+0+0 = 7 return 7 gMax=15 โ‘ฃ Node 20 leftGain=max(0,15)=15 rightGain=max(0,7)=7 split = 20+15+7 = 42 โ† NEW GLOBAL MAX! return 20+max(15,7) = 35 gMax=42 โ˜… best! โ‘ค Node -10 leftGain=max(0,9)=9 rightGain=max(0,35)=35 split = -10+9+35 = 34 < 42 โ†’ no update return -10+max(9,35) = 25 gMax=42 unchanged Answer: 42 โ€” path 15โ†’20โ†’7 โœ“
05
Section Five ยท Implementation

Code โ€” Java & Python

Java โ€” Post-order DFS
class Solution { int globalMax = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { dfs(root); return globalMax; } private int dfs(TreeNode node) { if (node == null) return 0; int leftGain = Math.max(0, dfs(node.left)); // prune negatives int rightGain = Math.max(0, dfs(node.right)); // Split path through this node (can't extend upward) globalMax = Math.max(globalMax, node.val + leftGain + rightGain); // Return best single arm to parent return node.val + Math.max(leftGain, rightGain); } }
Python โ€” Post-order DFS
class Solution: def maxPathSum(self, root) -> int: self.global_max = float('-inf') def dfs(node): if not node: return 0 left_gain = max(0, dfs(node.left)) right_gain = max(0, dfs(node.right)) self.global_max = max(self.global_max, node.val + left_gain + right_gain) return node.val + max(left_gain, right_gain) dfs(root) return self.global_max
06
Section Six ยท Analysis

Complexity Analysis

ApproachTimeSpace
Post-order DFSO(n)O(h) โ€” call stack
Each node visited once.:
  • At each node we do O(1) work โ€” compare gains, update global max. Total: O(n).
  • Space is the recursion depth = tree height.
07
Section Seven ยท Edge Cases

Edge Cases & Pitfalls

CaseInputWhy It Matters
All negative[-3,-2,-1]Max path is the single largest node (-1). max(0,...) prunes, but globalMax still considers each node alone.
Single node[5]Split = 5+0+0 = 5. Correct.
Negative root, positive children[-10,9,20]Split through root may be suboptimal. globalMax tracks child paths independently.
โš  Common Mistake: Returning the split path (left + node + right) to the parent. A split path through this node uses both branches โ€” it can't be extended upward because that would create a fork. Only return the best single-arm path: node.val + max(leftGain, rightGain).

โ† Back to Trees problems