Skip to content

147. Binary Tree Maximum Path Sum

πŸ”— LeetCode Problem: 124. Binary Tree Maximum Path Sum
πŸ“Š Difficulty: Hard
🏷️ Topics: Binary Trees, DFS, Recursion, Global Max, Post-order

Problem Statement

A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most once. Note that the path does not need to pass through the root.

The path sum of a path is the sum of the node's values in the path.

Given the root of a binary tree, return the maximum path sum of any non-empty path.

Example 1:

Input: root = [1,2,3]
Output: 6
Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.

Example 2:

Input: root = [-10,9,20,null,null,15,7]
Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.

Constraints: - The number of nodes in the tree is in the range [1, 3 * 10^4] - -1000 <= Node.val <= 1000


🌳 Visual Understanding - What is a Path?

Path Definition:

A PATH is a sequence of connected nodes
  - Each node appears AT MOST ONCE
  - Path does NOT need to go through root
  - Path can start and end ANYWHERE

Key insight: Path can be:
  1. Single node
  2. Going down (parent β†’ child β†’ grandchild)
  3. Going through node (left β†’ node β†’ right)
  4. Any combination!

Example 1 - Simple Tree:

Tree:
       1
      / \
     2   3

All possible paths:
  1. [2] β†’ sum = 2
  2. [1] β†’ sum = 1
  3. [3] β†’ sum = 3
  4. [2, 1] β†’ sum = 3
  5. [1, 3] β†’ sum = 4
  6. [2, 1, 3] β†’ sum = 6 ← MAXIMUM! βœ“

Maximum path sum: 6
Path: 2 β†’ 1 β†’ 3

Example 2 - With Negative Values:

Tree:
        -10
        /  \
       9    20
           /  \
          15   7

Key paths:
  1. [9] β†’ sum = 9
  2. [-10] β†’ sum = -10
  3. [20] β†’ sum = 20
  4. [15] β†’ sum = 15
  5. [7] β†’ sum = 7
  6. [15, 20] β†’ sum = 35
  7. [20, 7] β†’ sum = 27
  8. [15, 20, 7] β†’ sum = 42 ← MAXIMUM! βœ“
  9. [9, -10, 20] β†’ sum = 19
  10. [9, -10, 20, 15] β†’ sum = 34

Maximum path sum: 42
Path: 15 β†’ 20 β†’ 7 (doesn't include -10!)

Visual Path Types:

Type 1: Single node
    A

    Path: [A]

Type 2: Straight down
    A
   /
  B
 /
C

    Paths: [A], [A,B], [A,B,C]

Type 3: Through node (inverted V)
    B
   / \
  A   C

    Path: [A,B,C]
    This is KEY! Path going THROUGH B

Type 4: L-shaped (one branch only)
    A
   / \
  B   C
 /
D

    Path: [D,B,A] (not including C)

🧠 The AHA Moment - What to Return vs What to Track

The Critical Insight:

TWO different concepts:

1. RETURN VALUE (to parent):
   "Best path sum that can EXTEND UPWARD"
   = node + max(left_path, right_path, 0)

   Parent can extend this path!

2. GLOBAL MAX (tracked separately):
   "Best path sum going THROUGH this node"
   = node + left_path + right_path

   This path is COMPLETE, can't extend!

This is the CORE of the problem! πŸ”‘

Visual Explanation:

Tree:
       10
      /  \
     2    3

At node 10:

  OPTION A: Path extending UP
       ?
       |
      10
      / \
     2   3

     Can include: 10 + max(2, 3) = 13
     Return 13 (parent can extend)

  OPTION B: Path THROUGH 10
      10
      / \
     2   3

     Complete path: 2 + 10 + 3 = 15
     Track in global max (can't extend!)

  What to return? 13 (extendable)
  What to track? 15 (complete path through node)

Why This Matters:

If we return the complete path:
       10
      /  \
     2    3
    /      \
   5        7

At node 10:
  Complete path through 10: 5+2+10+3+7 = 27

  If we return 27 to parent:
       20
      /
     10 (returns 27)
    /  \
   2    3
  /      \
 5        7

 Parent calculates: 20 + 27 = 47
 But this path is INVALID!
   Would be: 5β†’2β†’10β†’3β†’7β†’20
   Node 10 appears in middle with BOTH children! βœ—

We must return EXTENDABLE paths only! 🎯

🧠 Recursive Thinking - Building the Solution

Base Case:

What's the simplest case?
  β†’ null node

What to return?
  β†’ 0 (no path, contributes 0)

Why not Integer.MIN_VALUE?
  β†’ Because parent should be able to IGNORE this branch
  β†’ Returning 0 means "no contribution"

if (node == null) return 0;

Recursive Case - Step by Step:

At each node, we need to:

STEP 1: Get best paths from children
  leftMax = maxPathSum(node.left)
  rightMax = maxPathSum(node.right)

  These are EXTENDABLE paths (or 0)

STEP 2: Consider IGNORING negative paths
  leftMax = max(0, leftMax)
  rightMax = max(0, rightMax)

  Why? Negative paths DECREASE sum!
  Better to not include them!

STEP 3: Calculate path THROUGH current node
  pathThrough = node.val + leftMax + rightMax

  This is a COMPLETE path (inverted V)
  Update global max with this!

STEP 4: Return EXTENDABLE path
  return node.val + max(leftMax, rightMax)

  Parent can only extend ONE branch!
  Choose the better one!

What Each Node Returns:

Each node returns:
  "Best path sum starting at this node going DOWN"

This can be:
  - Just the node itself
  - Node + left path
  - Node + right path

But NEVER node + both paths (not extendable!)

Example:
       10
      /  \
     2    3

Node 2 returns: 2
Node 3 returns: 3
Node 10 returns: 10 + max(2, 3) = 13

But tracks globally: 2 + 10 + 3 = 15

🎯 Solution: DFS with Global Maximum

Implementation:

class Solution {
    private int maxSum;  // Global variable to track maximum

    public int maxPathSum(TreeNode root) {
        // Initialize with smallest possible value
        // WHY? Any path will be larger than this
        maxSum = Integer.MIN_VALUE;

        // Start DFS
        maxGain(root);

        // Return the global maximum found
        return maxSum;
    }

    private int maxGain(TreeNode node) {
        // Base case: null node contributes 0
        if (node == null) {
            return 0;
        }

        // STEP 1: Get maximum gain from left subtree
        // Recursively calculate best path in left
        int leftGain = maxGain(node.left);

        // STEP 2: Get maximum gain from right subtree
        // Recursively calculate best path in right
        int rightGain = maxGain(node.right);

        // STEP 3: Ignore negative paths
        // WHY? Negative paths decrease sum, better to exclude
        // This means: "don't take this branch at all"
        leftGain = Math.max(0, leftGain);
        rightGain = Math.max(0, rightGain);

        // STEP 4: Calculate path THROUGH current node
        // This is: left_path + node + right_path
        // This forms an "inverted V" or straight line
        int pathThroughNode = node.val + leftGain + rightGain;

        // STEP 5: Update global maximum
        // This path might be the answer!
        // WHY separate variable? Because this path can't extend to parent
        maxSum = Math.max(maxSum, pathThroughNode);

        // STEP 6: Return maximum gain TO PARENT
        // Parent can only extend ONE branch (left OR right)
        // WHY? If we include both, path would split at parent
        // Choose the better branch!
        return node.val + Math.max(leftGain, rightGain);
    }
}

Why Global Variable?

Global maxSum tracks: "Best complete path found anywhere"

Can't return this because:
  - Parent needs extendable path
  - Complete path can't extend

Two separate concerns:
  1. What parent needs (return value)
  2. What might be answer (global max)

Global variable solves this elegantly! 🎯

πŸ” Detailed Dry Run - Complete Recursion

Tree:
       -10
       /  \
      9   20
          / \
         15  7

Complete Call Stack Visualization:

═══════════════════════════════════════════════════
INITIAL STATE:
═══════════════════════════════════════════════════

maxSum = -∞
Start: maxGain(-10)

═══════════════════════════════════════════════════
LEVEL 0: maxGain(-10)
═══════════════════════════════════════════════════

node = -10

β”œβ”€ LEFT: maxGain(9) ───────────────────────────────┐
β”‚                                                   β”‚
β”‚  LEVEL 1: maxGain(9)                             β”‚
β”‚  β”‚                                                β”‚
β”‚  β”œβ”€ node = 9                                     β”‚
β”‚  β”‚                                                β”‚
β”‚  β”œβ”€ LEFT: maxGain(null) β†’ return 0              β”‚
β”‚  β”œβ”€ RIGHT: maxGain(null) β†’ return 0             β”‚
β”‚  β”‚                                                β”‚
β”‚  β”œβ”€ leftGain = max(0, 0) = 0                    β”‚
β”‚  β”œβ”€ rightGain = max(0, 0) = 0                   β”‚
β”‚  β”‚                                                β”‚
β”‚  β”œβ”€ pathThrough = 9 + 0 + 0 = 9                 β”‚
β”‚  β”œβ”€ maxSum = max(-∞, 9) = 9                     β”‚
β”‚  β”‚                                                β”‚
β”‚  └─ return 9 + max(0, 0) = 9                    β”‚
β”‚                                                   β”‚
β”œβ”€ RIGHT: maxGain(20) ─────────────────────────────┐
β”‚                                                   β”‚
β”‚  LEVEL 1: maxGain(20)                            β”‚
β”‚  β”‚                                                β”‚
β”‚  β”œβ”€ node = 20                                    β”‚
β”‚  β”‚                                                β”‚
β”‚  β”œβ”€ LEFT: maxGain(15) ──────────────────────┐   β”‚
β”‚  β”‚                                           β”‚   β”‚
β”‚  β”‚  LEVEL 2: maxGain(15)                    β”‚   β”‚
β”‚  β”‚  β”‚                                        β”‚   β”‚
β”‚  β”‚  β”œβ”€ node = 15                            β”‚   β”‚
β”‚  β”‚  β”œβ”€ LEFT: maxGain(null) β†’ return 0       β”‚   β”‚
β”‚  β”‚  β”œβ”€ RIGHT: maxGain(null) β†’ return 0      β”‚   β”‚
β”‚  β”‚  β”‚                                        β”‚   β”‚
β”‚  β”‚  β”œβ”€ leftGain = 0, rightGain = 0          β”‚   β”‚
β”‚  β”‚  β”œβ”€ pathThrough = 15 + 0 + 0 = 15        β”‚   β”‚
β”‚  β”‚  β”œβ”€ maxSum = max(9, 15) = 15             β”‚   β”‚
β”‚  β”‚  β”‚                                        β”‚   β”‚
β”‚  β”‚  └─ return 15 + max(0, 0) = 15           β”‚   β”‚
β”‚  β”‚                                           β”‚   β”‚
β”‚  β”œβ”€ RIGHT: maxGain(7) ─────────────────────┐│   β”‚
β”‚  β”‚                                          β”‚β”‚   β”‚
β”‚  β”‚  LEVEL 2: maxGain(7)                    β”‚β”‚   β”‚
β”‚  β”‚  β”‚                                       β”‚β”‚   β”‚
β”‚  β”‚  β”œβ”€ node = 7                            β”‚β”‚   β”‚
β”‚  β”‚  β”œβ”€ LEFT: maxGain(null) β†’ return 0      β”‚β”‚   β”‚
β”‚  β”‚  β”œβ”€ RIGHT: maxGain(null) β†’ return 0     β”‚β”‚   β”‚
β”‚  β”‚  β”‚                                       β”‚β”‚   β”‚
β”‚  β”‚  β”œβ”€ leftGain = 0, rightGain = 0         β”‚β”‚   β”‚
β”‚  β”‚  β”œβ”€ pathThrough = 7 + 0 + 0 = 7         β”‚β”‚   β”‚
β”‚  β”‚  β”œβ”€ maxSum = max(15, 7) = 15 (no change)β”‚β”‚   β”‚
β”‚  β”‚  β”‚                                       β”‚β”‚   β”‚
β”‚  β”‚  └─ return 7 + max(0, 0) = 7            β”‚β”‚   β”‚
β”‚  β”‚                                          β”‚β”‚   β”‚
β”‚  β”œβ”€ Back at node 20:                        β”‚   β”‚
β”‚  β”œβ”€ leftGain from 15 = 15                   β”‚   β”‚
β”‚  β”œβ”€ rightGain from 7 = 7                    β”‚   β”‚
β”‚  β”‚                                           β”‚   β”‚
β”‚  β”œβ”€ leftGain = max(0, 15) = 15              β”‚   β”‚
β”‚  β”œβ”€ rightGain = max(0, 7) = 7               β”‚   β”‚
β”‚  β”‚                                           β”‚   β”‚
β”‚  β”œβ”€ pathThrough = 20 + 15 + 7 = 42          β”‚   β”‚
β”‚  β”œβ”€ maxSum = max(15, 42) = 42 ← NEW MAX! βœ“  β”‚   β”‚
β”‚  β”‚                                           β”‚   β”‚
β”‚  └─ return 20 + max(15, 7) = 35             β”‚   β”‚
β”‚                                                   β”‚
β”œβ”€ Back at node -10:                               β”‚
β”œβ”€ leftGain from 9 = 9                             β”‚
β”œβ”€ rightGain from 20 = 35                          β”‚
β”‚                                                   β”‚
β”œβ”€ leftGain = max(0, 9) = 9                        β”‚
β”œβ”€ rightGain = max(0, 35) = 35                     β”‚
β”‚                                                   β”‚
β”œβ”€ pathThrough = -10 + 9 + 35 = 34                 β”‚
β”œβ”€ maxSum = max(42, 34) = 42 (no change)           β”‚
β”‚                                                   β”‚
└─ return -10 + max(9, 35) = 25                    β”‚

═══════════════════════════════════════════════════
FINAL: maxSum = 42
═══════════════════════════════════════════════════

Maximum path: 15 β†’ 20 β†’ 7
Sum: 15 + 20 + 7 = 42 βœ“

Return Values vs Global Max:

Node returns (to parent) vs tracks (globally):

Node 15:
  Returns: 15
  Tracks: 15

Node 7:
  Returns: 7
  Tracks: 7

Node 20:
  Returns: 20 + max(15, 7) = 35
  Tracks: 20 + 15 + 7 = 42 ← ANSWER!

Node 9:
  Returns: 9
  Tracks: 9

Node -10:
  Returns: -10 + max(9, 35) = 25
  Tracks: -10 + 9 + 35 = 34

Global max across all: 42 βœ“

πŸ” Visual Path Analysis

Why Path 15β†’20β†’7 is Maximum:

Let's examine all major paths:

Path 1: Just node 20
       20
  Sum: 20

Path 2: 15 β†’ 20
        20
       /
      15
  Sum: 35

Path 3: 20 β†’ 7
        20
          \
           7
  Sum: 27

Path 4: 15 β†’ 20 β†’ 7 βœ“
        20
       /  \
      15   7
  Sum: 42 ← MAXIMUM!

Path 5: 9 β†’ -10 β†’ 20
        -10
       /    \
      9     20
  Sum: 19 (worse than path 4)

Path 6: 9 β†’ -10 β†’ 20 β†’ 15
        -10
       /    \
      9     20
           /
          15
  Sum: 34 (worse than path 4)

Maximum: 42 from path 15β†’20β†’7

Why We Ignore Negative Contributions:

Consider if we DON'T max with 0:

Tree:
       10
      /
    -5

Without max(0, left):
  leftGain = -5
  pathThrough = 10 + (-5) = 5
  return = 10 + (-5) = 5

With max(0, left):
  leftGain = max(0, -5) = 0
  pathThrough = 10 + 0 = 10 ← BETTER!
  return = 10 + 0 = 10

Ignoring negative paths gives better results! 🎯

πŸ“Š Complexity Analysis

Time Complexity: O(n)

Visit each node exactly once
At each node: O(1) operations
  - Two recursive calls (each visit child once)
  - Math.max operations
  - Addition

Total: n Γ— O(1) = O(n)

Space Complexity: O(h)

Recursion call stack: O(h) where h = height

Best case (balanced): O(log n)
Worst case (skewed): O(n)

Global variable maxSum: O(1)

Total: O(h)


⚠️ Common Mistakes

Mistake 1: Returning Path Through Node

// ❌ WRONG - Returns complete path
private int maxGain(TreeNode node) {
    if (node == null) return 0;

    int leftGain = maxGain(node.left);
    int rightGain = maxGain(node.right);

    leftGain = Math.max(0, leftGain);
    rightGain = Math.max(0, rightGain);

    // BUG: Returns path through node
    return node.val + leftGain + rightGain;  // βœ— NOT EXTENDABLE!
}

// Problem: Parent can't extend this!
// Would create invalid paths

// βœ“ CORRECT - Return extendable path
return node.val + Math.max(leftGain, rightGain);  βœ“

Mistake 2: Not Tracking Global Max

// ❌ WRONG - Only returns, doesn't track
private int maxGain(TreeNode node) {
    if (node == null) return 0;

    int leftGain = maxGain(node.left);
    int rightGain = maxGain(node.right);

    return node.val + Math.max(leftGain, rightGain);
}

// Problem: Misses path THROUGH nodes!
// Tree:  1
//       / \
//      2   3
// Returns: 1 + max(2, 3) = 4
// But path 2β†’1β†’3 = 6 is better! βœ—

// βœ“ CORRECT - Track global max
int pathThrough = node.val + leftGain + rightGain;
maxSum = Math.max(maxSum, pathThrough);  βœ“

Mistake 3: Not Handling Negative Paths

// ❌ WRONG - Includes negative paths
private int maxGain(TreeNode node) {
    if (node == null) return 0;

    int leftGain = maxGain(node.left);   // Might be negative!
    int rightGain = maxGain(node.right); // Might be negative!

    // Using negative values decreases sum!
    int pathThrough = node.val + leftGain + rightGain;
    maxSum = Math.max(maxSum, pathThrough);

    return node.val + Math.max(leftGain, rightGain);
}

// Tree:   10
//        /
//      -20
// leftGain = -20
// pathThrough = 10 + (-20) = -10
// Should be just 10! βœ—

// βœ“ CORRECT - Max with 0
leftGain = Math.max(0, leftGain);   βœ“
rightGain = Math.max(0, rightGain); βœ“

Mistake 4: Wrong Initial maxSum

// ❌ WRONG - Initial maxSum = 0
private int maxSum = 0;  // βœ—

// Problem: What if all nodes negative?
// Tree:  -1
//       /

🎯 Pattern Recognition - Post-order with Global Tracking

Core Pattern: Return vs Track

Template for problems where:
  - Need to consider ALL possible structures
  - Different from what parent can use

Structure:
  private GlobalValue global;

  private ReturnValue dfs(node) {
      if (node == null) return base;

      left = dfs(node.left);
      right = dfs(node.right);

      // Calculate complete structure at node
      complete = calculate(node, left, right);

      // Update global with complete
      global = update(global, complete);

      // Return what parent can use
      return extendable(node, left, right);
  }

1. Diameter of Binary Tree

Track: Path length through node
Return: Path length from node down
Same pattern!

2. Binary Tree Maximum Width

Track: Max width at any level
Return: Position info for parent
Global tracking!

3. Longest Univalue Path

Track: Path through node with same value
Return: Extendable univalue path
Same pattern!

4. Binary Tree Cameras

Track: Total cameras needed
Return: State (covered/has camera/needs camera)
Similar structure!

When to Use This Pattern:

βœ“ Answer is different from recursion return
βœ“ Need to consider complete structures
βœ“ Complete structure not extendable to parent
βœ“ Track global max/min/count
βœ“ Return value for parent's calculation

πŸ“ Quick Revision Notes

🎯 Core Concept

Maximum path sum where path can go anywhere. Key insight: What node RETURNS (extendable path for parent = node + max(left, right)) vs what it TRACKS (complete path through node = node + left + right). Use global maxSum to track best complete path. Max with 0 to choose whether to extend path (NOT ignoring nodes!). Post-order: process children first, then combine at node.

⚑ Quick Implementation

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }

    public static TreeNode buildTree(Integer[] arr) {
        if (arr == null || arr.length == 0 || arr[0] == null) {
            return null;
        }

        TreeNode root = new TreeNode(arr[0]);
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        int i = 1;
        while (!queue.isEmpty() && i < arr.length) {
            TreeNode current = queue.poll();

            // Process left child
            if (i < arr.length) {
                if (arr[i] != null) {
                    current.left = new TreeNode(arr[i]);
                    queue.offer(current.left);
                }
                i++;
            }

            // Process right child
            if (i < arr.length) {
                if (arr[i] != null) {
                    current.right = new TreeNode(arr[i]);
                    queue.offer(current.right);
                }
                i++;
            }
        }

        return root;
    }

    public static void print(TreeNode root) {
        if (root == null) {
            System.out.println("[]");
            return;
        }

        List<String> result = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if (node != null) {
                result.add(String.valueOf(node.val));
                queue.offer(node.left);
                queue.offer(node.right);
            } else {
                result.add("null");
            }
        }

        while (!result.isEmpty() && result.get(result.size() - 1).equals("null")) {
            result.remove(result.size() - 1);
        }

        System.out.println(result);
    }    
}

class Solution {
  public int maxPathSum(TreeNode root) {
    // Approach - check visual explanation
    // 2 important things for sum at a node: THROUGH and EXTENDINGUP
    // THROUGH: A node can be parent of and the path is going through it
    // EXTENDING_UP: A node can't be parent and it passes the sum upward to other node which would be parent.
    // If its THROUGH: node_val + left + right
    // If its EXTENDING_UP: node_val or node_val + max (left, right). You can only 1 path (left or right) with you in this case. Otherwise, it cannot be a VALID path when you extending up.
    // Last important case: a node alone can contribute max sum sometimes of left or right would come with -ve value. In that case, you take it as 0.
    int[] res = new int[] {Integer.MIN_VALUE};
    maxPathSum(root, res);

    return res[0];
  }

  private int maxPathSum(TreeNode root, int[] res) {
    // base case
    if(root == null) {
      return 0;
    }

    int left = maxPathSum(root.left, res);
    int right = maxPathSum(root.right, res);

    // If left or right comes as 0, ignore it.
    left = Math.max(left, 0);
    right = Math.max(right, 0);

    // THROUGH use case
    res[0] = Math.max(res[0], root.val + left + right);

    // EXTENDING_UP use case (this will not be result). So, just return to parent.
    // Node itself or Node + left or Node + right
    return Math.max(root.val, Math.max(root.val + left, root.val + right));
  }

  public static void main(String[] args) {
    Solution s = new Solution();

    System.out.println(s.maxPathSum(TreeNode.buildTree(new Integer[] {1,2,3}))); // 6
    System.out.println(s.maxPathSum(TreeNode.buildTree(new Integer[] {-10,9,20,null,null,15,7}))); // 42
  }
}

πŸ”‘ Key Insights

Return vs Track - The Core Distinction:

At each node, TWO calculations:

1. Path THROUGH node (complete, not extendable):
   node.val + leftGain + rightGain
   β†’ Track in global maxSum
   β†’ This might be the answer!
   β†’ EVERY node considered (even if gain = 0)

2. Path FROM node (extendable to parent):
   node.val + max(leftGain, rightGain)
   β†’ Return to parent
   β†’ Parent can extend this!

Why different?
  If return complete path:
       Parent
         |
       Node
       /  \
     Left Right

   Parent would try: Parent + (Node+Left+Right)
   But this creates: Left→Node→Right→Parent
   Node used as junction with BOTH children β†’ INVALID! βœ—

  Must return ONE branch only!

This is the HARDEST part to understand! πŸ”‘

Max with 0 - CRITICAL CLARIFICATION:

leftGain = Math.max(0, leftGain)
rightGain = Math.max(0, rightGain)

This is NOT "ignoring negative nodes"!
This is "choosing not to EXTEND path if it hurts"!

What it means:
  - If childGain > 0: Include child in path (helps!)
  - If childGain <= 0: Don't extend to child (hurts!)

But EVERY node is still considered individually!
Because we ALWAYS execute:
  maxSum = Math.max(maxSum, node.val + leftGain + rightGain)

Example 1: Mixed positive/negative
       10
      /
    -20

  leftGain = max(0, -20) = 0
  pathThrough = 10 + 0 = 10
  Answer: 10 (just node 10, don't extend to -20)

Example 2: ALL negative nodes
       -1
      /  \
    -2   -3

  Node -2: pathThrough = -2, tracked! βœ“
  Node -3: pathThrough = -3, tracked! βœ“
  Node -1: 
    leftGain = max(0, -2) = 0 (don't extend)
    rightGain = max(0, -3) = 0 (don't extend)
    pathThrough = -1 + 0 + 0 = -1, tracked! βœ“

  maxSum evolution: -∞ β†’ -2 β†’ -2 β†’ -1
  Answer: -1 (single node, best option!)

All nodes were considered!
We just chose not to combine them because
combining makes it worse:
  -1 alone (-1) better than -1+-2 (-3) or -1+-3 (-4)

"Don't extend path to child if it decreases sum!" 🎯

Why This Design Works for All Cases:

GUARANTEE: Every node considered as single-node path

Because this line ALWAYS executes:
  maxSum = Math.max(maxSum, node.val + leftGain + rightGain)

When both children hurt (negative):
  leftGain = 0, rightGain = 0

Then:
  maxSum = Math.max(maxSum, node.val + 0 + 0)
  maxSum = Math.max(maxSum, node.val)

So single node IS considered! βœ“

The algorithm evaluates:
  βœ“ Single nodes: [node]
  βœ“ Node + left: [left, node] if left helps
  βœ“ Node + right: [node, right] if right helps  
  βœ“ Complete: [left, node, right] if both help

"Help" means positive contribution (gain > 0)

Global Variable Necessity:

Why global maxSum?

Can't return it because:
  - Parent needs extendable value
  - Complete path not extendable

Two separate concerns:
  1. What helps parent (return)
  2. What might be answer (global)

Global variable elegantly solves this! ✨

Initialize with Integer.MIN_VALUE:
  - Handles all-negative trees
  - Any real path sum will be larger
  - First node updates it immediately

πŸŽͺ Memory Aid

"Track complete, return extendable!"
"Max(0, gain) = choose not to extend if hurts!"
"ALL nodes considered, just don't combine if worse!"
"Global max, local return!"
"Post-order: children first!" ✨

⚠️ Don't Forget

  • Track path THROUGH (node + left + right) - ALWAYS!
  • Return path FROM (node + max(left, right))
  • Max with 0 (choose not to extend, NOT ignore nodes!)
  • Global maxSum (track answer separately)
  • Initialize MIN_VALUE (handles all-negative trees!)
  • Every node evaluated (as single-node path minimum)
  • Post-order (process children first!)

πŸŽ‰ Congratulations!

You've mastered one of the HARDEST tree problems!

What You Learned:

βœ… Return vs Track - Core distinction
βœ… Global variable - Tracking separately
βœ… Post-order processing - Children first
βœ… Negative handling - Choice not to extend
βœ… Extendable paths - What parent can use
βœ… Complete paths - What might be answer
βœ… All-negative trees - Every node still considered

Why This Problem is Special:

This problem teaches:
  1. Return value β‰  Answer
  2. Global state for tracking
  3. Post-order aggregation
  4. Strategic path extension
  5. Path extendability concept
  6. Handling all edge cases

All fundamental concepts! πŸŽ“

Interview Mastery:

Interviewer: "Maximum path sum"

Your explanation:
  "Need to distinguish two things:

   1. Path THROUGH node: complete path
      β†’ Track in global max
      β†’ Might be answer
      β†’ Every node considered

   2. Path FROM node: extendable
      β†’ Return to parent
      β†’ Parent can extend

   Use post-order DFS with global tracking.
   Max with 0 to choose whether to extend path.
   Handles all-negative trees correctly.

   Time: O(n), Space: O(h)"

Shows deep understanding! πŸ’―

You've conquered a HARD problem with complete understanding! πŸ†βœ¨πŸŽ―