Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

124. Binary Tree Maximum Path Sum - Leetcode Solution

Problem Description

Given the root of a binary tree, the task is to find the maximum path sum. A path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.

  • Each node contains an integer value (can be negative or positive).
  • The path can start and end at any nodes in the tree.
  • You may only travel from parent nodes to children (not vice versa).
  • The path must be continuous (no skipping nodes).

You are to return an integer representing the maximum path sum among all possible paths in the tree.

Thought Process

At first glance, you might consider every possible path in the tree and compute the sum for each, but this would be inefficient for large trees. Instead, let's think about how paths can be constructed:

  • Any path can be defined by its highest node (the "peak"), from which it extends down zero or more nodes to the left and/or right.
  • For each node, the best path that uses that node as a peak is: left-max + node's value + right-max, where left-max and right-max are the best downward path sums from the left and right child, respectively.
  • But when returning to the parent, we can only choose one side (left or right), since a parent can't take both sides (otherwise, the path would fork).
  • Therefore, for each node, we need to consider both: the best path through the node (for the global answer), and the best path downward from the node (to propagate up the tree).

This insight leads us to a recursive, post-order traversal, where each node calculates the best sum it can contribute upward, and updates a global maximum if a better path is found.

Solution Approach

  • Recursive Traversal: Use a helper function that recursively computes the maximum gain from each subtree.
  • Base Case: If the current node is null, return 0 (no gain).
  • Recursive Step:
    • Compute the maximum gain from the left and right children. If the gain is negative, treat it as 0 (since adding a negative path would decrease the sum).
    • The best path through the current node is: node.val + left_gain + right_gain.
    • Update the global maximum if this path is greater than the previous maximum.
    • Return the maximum gain that can be contributed to the parent: node.val + max(left_gain, right_gain).
  • Initialization: Start with a global variable (e.g., max_sum) initialized to negative infinity.
  • Result: After traversal, max_sum will hold the answer.

We use post-order traversal because we need to compute left and right gains before we can process the current node. The use of a global variable ensures that the maximum is updated regardless of where the optimal path occurs in the tree.

Example Walkthrough

Consider the following tree:

        -10
        /  \
       9   20
           / \
          15  7
  
  • Step 1: Start at the leaves. For node 15 and 7, their max gain is their value (since they have no children): 15 and 7.
  • Step 2: For node 20:
    • Left gain = 15, Right gain = 7
    • Path through 20: 20 + 15 + 7 = 42
    • Update global max to 42
    • Return to parent: 20 + max(15,7) = 35
  • Step 3: For node 9, no children, so gain is 9.
  • Step 4: For root -10:
    • Left gain = 9, Right gain = 35
    • Path through -10: -10 + 9 + 35 = 34
    • Global max remains 42 (from step 2)
    • Return to parent: -10 + max(9, 35) = 25
  • Result: The maximum path sum is 42, from the path 15 → 20 → 7.

Time and Space Complexity

  • Brute-force Approach: If you tried to enumerate every possible path, the number of paths grows exponentially with the number of nodes, leading to O(2^n) time complexity.
  • Optimized Approach: The recursive solution visits each node exactly once, performing constant work per node. Therefore, the time complexity is O(n), where n is the number of nodes.
  • Space Complexity: The space used is O(h), where h is the height of the tree, due to the recursion stack. In the worst case (skewed tree), this is O(n); in a balanced tree, O(log n).

Summary

The key insight is that the maximum path sum can be found by considering each node as a potential "peak" of a path, and recursively computing the best gains from its subtrees. By using post-order traversal, we efficiently propagate the best contributions upward, while updating a global maximum whenever a better path is found. This approach is elegant, efficient, and leverages the recursive structure of trees naturally.

Code Implementation

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def maxPathSum(self, root: TreeNode) -> int:
        self.max_sum = float('-inf')
        
        def max_gain(node):
            if not node:
                return 0
            # Discard negative paths
            left_gain = max(max_gain(node.left), 0)
            right_gain = max(max_gain(node.right), 0)
            # Path through this node
            current_sum = node.val + left_gain + right_gain
            self.max_sum = max(self.max_sum, current_sum)
            # Return max gain to parent
            return node.val + max(left_gain, right_gain)
        
        max_gain(root)
        return self.max_sum
      
// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
    int max_sum = INT_MIN;
    
    int maxGain(TreeNode* node) {
        if (!node) return 0;
        int left_gain = std::max(maxGain(node->left), 0);
        int right_gain = std::max(maxGain(node->right), 0);
        int current_sum = node->val + left_gain + right_gain;
        max_sum = std::max(max_sum, current_sum);
        return node->val + std::max(left_gain, right_gain);
    }
    
    int maxPathSum(TreeNode* root) {
        maxGain(root);
        return max_sum;
    }
};
      
// Definition for a binary tree node.
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

class Solution {
    private int maxSum = Integer.MIN_VALUE;
    
    public int maxPathSum(TreeNode root) {
        maxGain(root);
        return maxSum;
    }
    
    private int maxGain(TreeNode node) {
        if (node == null) return 0;
        int leftGain = Math.max(maxGain(node.left), 0);
        int rightGain = Math.max(maxGain(node.right), 0);
        int currentSum = node.val + leftGain + rightGain;
        maxSum = Math.max(maxSum, currentSum);
        return node.val + Math.max(leftGain, rightGain);
    }
}
      
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
var maxPathSum = function(root) {
    let maxSum = -Infinity;
    
    function maxGain(node) {
        if (node === null) return 0;
        let leftGain = Math.max(maxGain(node.left), 0);
        let rightGain = Math.max(maxGain(node.right), 0);
        let currentSum = node.val + leftGain + rightGain;
        maxSum = Math.max(maxSum, currentSum);
        return node.val + Math.max(leftGain, rightGain);
    }
    
    maxGain(root);
    return maxSum;
};