Bird
Raised Fist0
Interview Prepchallenge-problemshardFacebookAmazonGoogleMicrosoft

Binary Tree Maximum Path Sum

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
🎯
Binary Tree Maximum Path Sum
hardMIXEDFacebookAmazonGoogle

Imagine you are analyzing a network of connected nodes representing a power grid, and you want to find the path that yields the maximum total power output, even if it passes through the central hub and branches out.

💡 This problem involves finding the maximum sum path in a binary tree, which is challenging because the path can start and end at any node, not necessarily the root or leaves. Beginners often struggle to understand how to consider paths that bend at a node and how to efficiently compute the maximum sum without redundant calculations.
📋
Problem Statement

Given a non-empty binary tree, 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.

The number of nodes in the tree is in the range [1, 10^5].-10^4 <= Node.val <= 10^4
💡
Example
Input"[1,2,3]"
Output6

The path with maximum sum is 2 -> 1 -> 3, which sums to 6.

Input"[-10,9,20,null,null,15,7]"
Output42

The path with maximum sum is 15 -> 20 -> 7, which sums to 42.

  • Single node tree → output is the node's value
  • All nodes have negative values → output is the maximum single node value
  • Tree with only left children → path is the sum of all nodes
  • Tree with only right children → path is the sum of all nodes
⚠️
Common Mistakes
Not ignoring negative gains from subtrees

Path sum decreases incorrectly, leading to wrong max sum

Use max(child_gain, 0) to ignore negative contributions

Returning sum of both children to parent

Incorrect path sums and double counting, causing wrong results

Return node value plus max of left or right gain only

Not updating global max path sum at each node

Misses paths that bend at current node, leading to suboptimal answer

Update global max with node.val + left_gain + right_gain at each node

Using recursion without base case for null nodes

Runtime errors or infinite recursion

Return 0 for null nodes in recursion

Assuming path must start at root or leaf

Incorrect max path sum calculation

Allow path to start and end at any node

🧠
Brute Force (Pure Recursion / Nested Loops / etc.)
💡 Starting with brute force helps understand the problem's complexity by trying all possible paths, even though it's inefficient. It lays the foundation for more optimized solutions.

Intuition

Try every possible path in the tree by exploring all nodes and all paths starting from each node, then find the maximum sum among them.

Algorithm

  1. Define a helper function to compute the maximum path sum starting from a given node.
  2. For each node in the tree, compute the maximum path sum that passes through it by exploring all possible paths.
  3. Keep track of the global maximum path sum found so far.
  4. Return the global maximum after exploring all nodes.
💡 This approach is hard because it redundantly computes path sums multiple times and doesn't reuse results, leading to exponential time.
</>
Code
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def maxPathSum(root):
    max_sum = float('-inf')

    def max_gain(node):
        nonlocal max_sum
        if not node:
            return 0
        left_gain = max(max_gain(node.left), 0)
        right_gain = max(max_gain(node.right), 0)
        price_newpath = node.val + left_gain + right_gain
        max_sum = max(max_sum, price_newpath)
        return node.val + max(left_gain, right_gain)

    max_gain(root)
    return max_sum

# Driver code
if __name__ == '__main__':
    root = TreeNode(-10)
    root.left = TreeNode(9)
    root.right = TreeNode(20, TreeNode(15), TreeNode(7))
    print(maxPathSum(root))  # Output: 42
Line Notes
def maxPathSum(root):Defines the main function to compute max path sum
max_sum = float('-inf')Initialize global max to smallest possible to handle negative values
def max_gain(node):Helper function to compute max gain from current node
if not node:Base case: null nodes contribute 0 to path sum
left_gain = max(max_gain(node.left), 0)Ignore negative gains from left subtree
right_gain = max(max_gain(node.right), 0)Ignore negative gains from right subtree
price_newpath = node.val + left_gain + right_gainSum if path passes through current node including both children
max_sum = max(max_sum, price_newpath)Update global max if current path is better
return node.val + max(left_gain, right_gain)Return max gain to parent, only one side can be extended
class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int val) { this.val = val; }
}

public 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 priceNewPath = node.val + leftGain + rightGain;
        maxSum = Math.max(maxSum, priceNewPath);
        return node.val + Math.max(leftGain, rightGain);
    }

    public static void main(String[] args) {
        TreeNode root = new TreeNode(-10);
        root.left = new TreeNode(9);
        root.right = new TreeNode(20);
        root.right.left = new TreeNode(15);
        root.right.right = new TreeNode(7);
        Solution sol = new Solution();
        System.out.println(sol.maxPathSum(root)); // Output: 42
    }
}
Line Notes
private int maxSum = Integer.MIN_VALUE;Initialize global max to smallest integer to handle negatives
public int maxPathSum(TreeNode root) {Main function to start recursion
if (node == null) return 0;Base case: null nodes contribute zero
int leftGain = Math.max(maxGain(node.left), 0);Ignore negative left subtree sums
int rightGain = Math.max(maxGain(node.right), 0);Ignore negative right subtree sums
int priceNewPath = node.val + leftGain + rightGain;Sum if path passes through current node
maxSum = Math.max(maxSum, priceNewPath);Update global max if better path found
return node.val + Math.max(leftGain, rightGain);Return max gain to parent, only one side extended
#include <iostream>
#include <algorithm>
#include <climits>
using namespace std;

struct TreeNode {
    int val;
    TreeNode *left, *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
    int maxSum = INT_MIN;
public:
    int maxPathSum(TreeNode* root) {
        maxGain(root);
        return maxSum;
    }
private:
    int maxGain(TreeNode* node) {
        if (!node) return 0;
        int leftGain = max(maxGain(node->left), 0);
        int rightGain = max(maxGain(node->right), 0);
        int priceNewPath = node->val + leftGain + rightGain;
        maxSum = max(maxSum, priceNewPath);
        return node->val + max(leftGain, rightGain);
    }
};

int main() {
    TreeNode* root = new TreeNode(-10);
    root->left = new TreeNode(9);
    root->right = new TreeNode(20);
    root->right->left = new TreeNode(15);
    root->right->right = new TreeNode(7);
    Solution sol;
    cout << sol.maxPathSum(root) << endl; // Output: 42
    return 0;
}
Line Notes
int maxSum = INT_MIN;Initialize global max to smallest int to handle negative values
int maxPathSum(TreeNode* root) {Main function to start recursion
if (!node) return 0;Base case: null nodes contribute zero
int leftGain = max(maxGain(node->left), 0);Ignore negative left subtree sums
int rightGain = max(maxGain(node->right), 0);Ignore negative right subtree sums
int priceNewPath = node->val + leftGain + rightGain;Sum if path passes through current node
maxSum = max(maxSum, priceNewPath);Update global max if better path found
return node->val + max(leftGain, rightGain);Return max gain to parent, only one side extended
class TreeNode {
    constructor(val=0, left=null, right=null) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

function maxPathSum(root) {
    let maxSum = -Infinity;

    function maxGain(node) {
        if (!node) return 0;
        let leftGain = Math.max(maxGain(node.left), 0);
        let rightGain = Math.max(maxGain(node.right), 0);
        let priceNewPath = node.val + leftGain + rightGain;
        maxSum = Math.max(maxSum, priceNewPath);
        return node.val + Math.max(leftGain, rightGain);
    }

    maxGain(root);
    return maxSum;
}

// Driver code
const root = new TreeNode(-10,
    new TreeNode(9),
    new TreeNode(20, new TreeNode(15), new TreeNode(7))
);
console.log(maxPathSum(root)); // Output: 42
Line Notes
let maxSum = -Infinity;Initialize global max to smallest number to handle negatives
function maxGain(node) {Helper function to compute max gain from current node
if (!node) return 0;Base case: null nodes contribute zero
let leftGain = Math.max(maxGain(node.left), 0);Ignore negative left subtree sums
let rightGain = Math.max(maxGain(node.right), 0);Ignore negative right subtree sums
let priceNewPath = node.val + leftGain + rightGain;Sum if path passes through current node
maxSum = Math.max(maxSum, priceNewPath);Update global max if better path found
return node.val + Math.max(leftGain, rightGain);Return max gain to parent, only one side extended
Complexity
TimeO(n)
SpaceO(h)

Each node is visited once in postorder traversal; recursion stack uses space proportional to tree height h.

💡 For n=100,000 nodes and balanced tree height ~17, this means about 100,000 operations and stack depth 17, which is efficient.
Interview Verdict: Accepted

Though this looks like brute force, this recursive approach is actually optimal for this problem and accepted in interviews.

🧠
Iterative Postorder Traversal with Stack
💡 This approach replaces recursion with an explicit stack to avoid recursion overhead and stack overflow in very deep trees, which is important in production environments.

Intuition

Use an iterative postorder traversal to compute max gains bottom-up, simulating recursion with a stack and tracking visited nodes.

Algorithm

  1. Use a stack to traverse the tree in postorder iteratively.
  2. Track nodes and whether their children have been processed.
  3. For each node, compute max gains from left and right children once processed.
  4. Update global max path sum and store max gain for parent computations.
💡 This approach is harder to implement but avoids recursion limits and clarifies the traversal order.
</>
Code
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def maxPathSum(root):
    if not root:
        return 0
    max_sum = float('-inf')
    stack = []
    node = root
    last_visited = None
    gains = {}

    while stack or node:
        if node:
            stack.append(node)
            node = node.left
        else:
            peek = stack[-1]
            if peek.right and last_visited != peek.right:
                node = peek.right
            else:
                left_gain = max(gains.get(peek.left, 0), 0)
                right_gain = max(gains.get(peek.right, 0), 0)
                price_newpath = peek.val + left_gain + right_gain
                max_sum = max(max_sum, price_newpath)
                gains[peek] = peek.val + max(left_gain, right_gain)
                last_visited = stack.pop()
                node = None
    return max_sum

# Driver code
if __name__ == '__main__':
    root = TreeNode(-10)
    root.left = TreeNode(9)
    root.right = TreeNode(20, TreeNode(15), TreeNode(7))
    print(maxPathSum(root))  # Output: 42
Line Notes
stack = []Initialize stack to simulate recursion
while stack or node:Loop until all nodes processed
if node:Go as left as possible
if peek.right and last_visited != peek.right:Process right child if not visited
left_gain = max(gains.get(peek.left, 0), 0)Get left child's max gain or 0 if none
right_gain = max(gains.get(peek.right, 0), 0)Get right child's max gain or 0 if none
max_sum = max(max_sum, price_newpath)Update global max path sum
gains[peek] = peek.val + max(left_gain, right_gain)Store max gain for parent use
import java.util.*;

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

public class Solution {
    public int maxPathSum(TreeNode root) {
        if (root == null) return 0;
        int maxSum = Integer.MIN_VALUE;
        Map<TreeNode, Integer> gains = new HashMap<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode node = root, lastVisited = null;

        while (!stack.isEmpty() || node != null) {
            if (node != null) {
                stack.push(node);
                node = node.left;
            } else {
                TreeNode peek = stack.peek();
                if (peek.right != null && lastVisited != peek.right) {
                    node = peek.right;
                } else {
                    int leftGain = Math.max(gains.getOrDefault(peek.left, 0), 0);
                    int rightGain = Math.max(gains.getOrDefault(peek.right, 0), 0);
                    int priceNewPath = peek.val + leftGain + rightGain;
                    maxSum = Math.max(maxSum, priceNewPath);
                    gains.put(peek, peek.val + Math.max(leftGain, rightGain));
                    lastVisited = stack.pop();
                    node = null;
                }
            }
        }
        return maxSum;
    }

    public static void main(String[] args) {
        TreeNode root = new TreeNode(-10);
        root.left = new TreeNode(9);
        root.right = new TreeNode(20);
        root.right.left = new TreeNode(15);
        root.right.right = new TreeNode(7);
        Solution sol = new Solution();
        System.out.println(sol.maxPathSum(root)); // Output: 42
    }
}
Line Notes
Map<TreeNode, Integer> gains = new HashMap<>();Store max gains for nodes to avoid recomputation
Deque<TreeNode> stack = new ArrayDeque<>();Stack to simulate recursion
while (!stack.isEmpty() || node != null) {Traverse until all nodes processed
if (peek.right != null && lastVisited != peek.right) {Process right child if not visited
int leftGain = Math.max(gains.getOrDefault(peek.left, 0), 0);Get left child's max gain or 0
int rightGain = Math.max(gains.getOrDefault(peek.right, 0), 0);Get right child's max gain or 0
maxSum = Math.max(maxSum, priceNewPath);Update global max path sum
gains.put(peek, peek.val + Math.max(leftGain, rightGain));Store max gain for parent use
#include <iostream>
#include <stack>
#include <unordered_map>
#include <algorithm>
#include <climits>
using namespace std;

struct TreeNode {
    int val;
    TreeNode *left, *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
    int maxPathSum(TreeNode* root) {
        if (!root) return 0;
        int maxSum = INT_MIN;
        stack<TreeNode*> stk;
        unordered_map<TreeNode*, int> gains;
        TreeNode* node = root;
        TreeNode* lastVisited = nullptr;

        while (!stk.empty() || node) {
            if (node) {
                stk.push(node);
                node = node->left;
            } else {
                TreeNode* peek = stk.top();
                if (peek->right && lastVisited != peek->right) {
                    node = peek->right;
                } else {
                    int leftGain = max(gains[peek->left], 0);
                    int rightGain = max(gains[peek->right], 0);
                    int priceNewPath = peek->val + leftGain + rightGain;
                    maxSum = max(maxSum, priceNewPath);
                    gains[peek] = peek->val + max(leftGain, rightGain);
                    lastVisited = peek;
                    stk.pop();
                    node = nullptr;
                }
            }
        }
        return maxSum;
    }
};

int main() {
    TreeNode* root = new TreeNode(-10);
    root->left = new TreeNode(9);
    root->right = new TreeNode(20);
    root->right->left = new TreeNode(15);
    root->right->right = new TreeNode(7);
    Solution sol;
    cout << sol.maxPathSum(root) << endl; // Output: 42
    return 0;
}
Line Notes
stack<TreeNode*> stk;Stack to simulate recursion
unordered_map<TreeNode*, int> gains;Map to store max gains for nodes
while (!stk.empty() || node) {Traverse until all nodes processed
if (peek->right && lastVisited != peek->right) {Process right child if not visited
int leftGain = max(gains[peek->left], 0);Get left child's max gain or 0
int rightGain = max(gains[peek->right], 0);Get right child's max gain or 0
maxSum = max(maxSum, priceNewPath);Update global max path sum
gains[peek] = peek->val + max(leftGain, rightGain);Store max gain for parent use
class TreeNode {
    constructor(val=0, left=null, right=null) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

function maxPathSum(root) {
    if (!root) return 0;
    let maxSum = -Infinity;
    const stack = [];
    const gains = new Map();
    let node = root;
    let lastVisited = null;

    while (stack.length > 0 || node !== null) {
        if (node !== null) {
            stack.push(node);
            node = node.left;
        } else {
            const peek = stack[stack.length - 1];
            if (peek.right !== null && lastVisited !== peek.right) {
                node = peek.right;
            } else {
                const leftGain = Math.max(gains.get(peek.left) || 0, 0);
                const rightGain = Math.max(gains.get(peek.right) || 0, 0);
                const priceNewPath = peek.val + leftGain + rightGain;
                maxSum = Math.max(maxSum, priceNewPath);
                gains.set(peek, peek.val + Math.max(leftGain, rightGain));
                lastVisited = stack.pop();
                node = null;
            }
        }
    }
    return maxSum;
}

// Driver code
const root = new TreeNode(-10,
    new TreeNode(9),
    new TreeNode(20, new TreeNode(15), new TreeNode(7))
);
console.log(maxPathSum(root)); // Output: 42
Line Notes
const stack = [];Stack to simulate recursion
const gains = new Map();Map to store max gains for nodes
while (stack.length > 0 || node !== null) {Traverse until all nodes processed
if (peek.right !== null && lastVisited !== peek.right) {Process right child if not visited
const leftGain = Math.max(gains.get(peek.left) || 0, 0);Get left child's max gain or 0
const rightGain = Math.max(gains.get(peek.right) || 0, 0);Get right child's max gain or 0
maxSum = Math.max(maxSum, priceNewPath);Update global max path sum
gains.set(peek, peek.val + Math.max(leftGain, rightGain));Store max gain for parent use
Complexity
TimeO(n)
SpaceO(n)

Each node is visited once; stack and map use O(n) space in worst case for skewed trees.

💡 For large trees, this approach avoids recursion stack overflow but uses extra memory for the stack and map.
Interview Verdict: Accepted

This approach is practical for very deep trees where recursion might fail, though more complex to implement.

🧠
Optimized Recursive Approach with Global Tracking
💡 This is the standard optimal solution that uses recursion with a global variable to track the maximum path sum, combining clarity and efficiency.

Intuition

Use postorder traversal to compute max gain from each node, updating a global max path sum that considers paths passing through the node including both children.

Algorithm

  1. Define a recursive helper function that returns the max gain from a node to its parent.
  2. At each node, compute max gains from left and right children, ignoring negative gains.
  3. Calculate the price of a new path passing through the node (node value + left gain + right gain).
  4. Update the global maximum path sum if the new path is better.
  5. Return the max gain to the parent (node value + max of left or right gain).
💡 This approach is elegant because it combines the exploration and global update in one traversal without extra data structures.
</>
Code
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def maxPathSum(root):
    max_sum = float('-inf')

    def helper(node):
        nonlocal max_sum
        if not node:
            return 0
        left = max(helper(node.left), 0)
        right = max(helper(node.right), 0)
        max_sum = max(max_sum, node.val + left + right)
        return node.val + max(left, right)

    helper(root)
    return max_sum

# Driver code
if __name__ == '__main__':
    root = TreeNode(-10)
    root.left = TreeNode(9)
    root.right = TreeNode(20, TreeNode(15), TreeNode(7))
    print(maxPathSum(root))  # Output: 42
Line Notes
max_sum = float('-inf')Initialize global max to smallest number to handle negatives
def helper(node):Recursive helper to compute max gain from node
if not node:Base case: null nodes contribute zero
left = max(helper(node.left), 0)Ignore negative left subtree sums
right = max(helper(node.right), 0)Ignore negative right subtree sums
max_sum = max(max_sum, node.val + left + right)Update global max path sum including both children
return node.val + max(left, right)Return max gain to parent, only one side extended
class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int val) { this.val = val; }
}

public class Solution {
    private int maxSum = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        helper(root);
        return maxSum;
    }

    private int helper(TreeNode node) {
        if (node == null) return 0;
        int left = Math.max(helper(node.left), 0);
        int right = Math.max(helper(node.right), 0);
        maxSum = Math.max(maxSum, node.val + left + right);
        return node.val + Math.max(left, right);
    }

    public static void main(String[] args) {
        TreeNode root = new TreeNode(-10);
        root.left = new TreeNode(9);
        root.right = new TreeNode(20);
        root.right.left = new TreeNode(15);
        root.right.right = new TreeNode(7);
        Solution sol = new Solution();
        System.out.println(sol.maxPathSum(root)); // Output: 42
    }
}
Line Notes
private int maxSum = Integer.MIN_VALUE;Initialize global max to smallest integer to handle negatives
public int maxPathSum(TreeNode root) {Main function to start recursion
if (node == null) return 0;Base case: null nodes contribute zero
int left = Math.max(helper(node.left), 0);Ignore negative left subtree sums
int right = Math.max(helper(node.right), 0);Ignore negative right subtree sums
maxSum = Math.max(maxSum, node.val + left + right);Update global max path sum including both children
return node.val + Math.max(left, right);Return max gain to parent, only one side extended
#include <iostream>
#include <algorithm>
#include <climits>
using namespace std;

struct TreeNode {
    int val;
    TreeNode *left, *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
    int maxSum = INT_MIN;
public:
    int maxPathSum(TreeNode* root) {
        helper(root);
        return maxSum;
    }
private:
    int helper(TreeNode* node) {
        if (!node) return 0;
        int left = max(helper(node->left), 0);
        int right = max(helper(node->right), 0);
        maxSum = max(maxSum, node->val + left + right);
        return node->val + max(left, right);
    }
};

int main() {
    TreeNode* root = new TreeNode(-10);
    root->left = new TreeNode(9);
    root->right = new TreeNode(20);
    root->right->left = new TreeNode(15);
    root->right->right = new TreeNode(7);
    Solution sol;
    cout << sol.maxPathSum(root) << endl; // Output: 42
    return 0;
}
Line Notes
int maxSum = INT_MIN;Initialize global max to smallest int to handle negative values
int maxPathSum(TreeNode* root) {Main function to start recursion
if (!node) return 0;Base case: null nodes contribute zero
int left = max(helper(node->left), 0);Ignore negative left subtree sums
int right = max(helper(node->right), 0);Ignore negative right subtree sums
maxSum = max(maxSum, node->val + left + right);Update global max path sum including both children
return node->val + max(left, right);Return max gain to parent, only one side extended
class TreeNode {
    constructor(val=0, left=null, right=null) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

function maxPathSum(root) {
    let maxSum = -Infinity;

    function helper(node) {
        if (!node) return 0;
        let left = Math.max(helper(node.left), 0);
        let right = Math.max(helper(node.right), 0);
        maxSum = Math.max(maxSum, node.val + left + right);
        return node.val + Math.max(left, right);
    }

    helper(root);
    return maxSum;
}

// Driver code
const root = new TreeNode(-10,
    new TreeNode(9),
    new TreeNode(20, new TreeNode(15), new TreeNode(7))
);
console.log(maxPathSum(root)); // Output: 42
Line Notes
let maxSum = -Infinity;Initialize global max to smallest number to handle negatives
function helper(node) {Recursive helper to compute max gain from node
if (!node) return 0;Base case: null nodes contribute zero
let left = Math.max(helper(node.left), 0);Ignore negative left subtree sums
let right = Math.max(helper(node.right), 0);Ignore negative right subtree sums
maxSum = Math.max(maxSum, node.val + left + right);Update global max path sum including both children
return node.val + Math.max(left, right);Return max gain to parent, only one side extended
Complexity
TimeO(n)
SpaceO(h)

Each node visited once; recursion stack uses space proportional to tree height h.

💡 For n=100,000 nodes and balanced tree height ~17, this means about 100,000 operations and stack depth 17, which is efficient.
Interview Verdict: Accepted

This is the preferred approach in interviews due to its clarity and efficiency.

📊
All Approaches - One-Glance Tradeoffs
💡 The optimal recursive approach is the best choice for interviews due to its clarity and efficiency. Iterative approach is useful for very deep trees. Brute force is mainly for understanding.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute Force (Recursive with global tracking)O(n)O(h)Yes (deep recursion)No (only sum returned)Code this approach; it is optimal and accepted
2. Iterative Postorder TraversalO(n)O(n)NoNoMention if recursion depth is a concern
3. Naive Brute Force (Try all paths)O(n^2) or worseO(h)YesNoMention only to explain problem complexity
💼
Interview Strategy
💡 Use this guide to understand the problem deeply before interviews. Start by clarifying the problem, then explain the brute force approach to show understanding, and finally implement the optimal solution. Practice coding and testing edge cases.

How to Present

Step 1: Clarify the problem and constraints with the interviewer.Step 2: Describe the brute force approach to demonstrate understanding.Step 3: Explain why brute force is inefficient and introduce the optimized recursive solution.Step 4: Write clean, well-commented code for the optimal approach.Step 5: Test your code with provided and edge cases.

Time Allocation

Clarify: 3min → Approach: 5min → Code: 10min → Test: 5min. Total ~23min

What the Interviewer Tests

The interviewer tests your understanding of tree traversal, handling negative values, and ability to optimize recursion with global tracking. They also assess your coding clarity and testing thoroughness.

Common Follow-ups

  • How would you handle very deep trees to avoid stack overflow? → Use iterative traversal or tail recursion optimization.
  • Can you modify the solution to return the actual path, not just the sum? → Track nodes along with sums during recursion.
💡 These follow-ups test your ability to adapt solutions for practical constraints and extend functionality.
🔍
Pattern Recognition

When to Use

1) Problem involves binary tree paths, 2) Path can start and end anywhere, 3) Need max sum or max value, 4) Negative values present

Signature Phrases

maximum path sumpath can start and end at any nodebinary tree

NOT This Pattern When

Longest root-to-leaf path sum problems, which restrict paths to root-leaf only

Similar Problems

Binary Tree Diameter - similar tree traversal and path conceptsPath Sum III - related to path sums but with different constraints

Practice

(1/5)
1. You need to visit all nodes of a binary tree in the order: root node first, then recursively the left subtree, followed by the right subtree. Which algorithmic approach guarantees this traversal order efficiently without extra space for recursion or stack?
easy
A. Postorder traversal using two stacks
B. Level-order traversal using a queue
C. Morris preorder traversal using threaded binary tree
D. Inorder traversal using recursion

Solution

  1. Step 1: Understand traversal order requirement

    The problem requires visiting root before left and right subtrees, which is preorder traversal.
  2. Step 2: Identify approach with no extra space

    Morris preorder traversal uses threaded binary trees to achieve preorder traversal without recursion or stack, thus optimal in space.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Preorder visits root first; Morris traversal achieves this with O(1) space [OK]
Hint: Preorder visits root before children [OK]
Common Mistakes:
  • Confusing inorder or postorder with preorder traversal
  • Assuming recursion is always needed
  • Using level-order which visits nodes by depth
2. You are given a binary tree that is complete (all levels except possibly the last are fully filled, and nodes in the last level are as far left as possible). You need to count the total number of nodes efficiently. Which approach guarantees the optimal time complexity for this problem?
easy
A. Perform a simple DFS traversal counting all nodes, which takes O(n) time.
B. Use the tree height to check if subtrees are perfect and recursively count nodes, achieving O((log n)^2) time.
C. Apply a greedy approach by counting nodes level by level until the last incomplete level.
D. Use a breadth-first search (BFS) traversal to count nodes, which is efficient for complete trees.

Solution

  1. Step 1: Understand the problem constraints

    The tree is complete, so the height can be used to identify perfect subtrees quickly.
  2. Step 2: Recognize the optimal approach

    Using the height to check if left or right subtree is perfect allows skipping large parts of the tree, reducing complexity to O((log n)^2).
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Optimal approach leverages tree height and completeness [OK]
Hint: Use tree height to skip perfect subtrees [OK]
Common Mistakes:
  • Assuming BFS or simple DFS is optimal
  • Using greedy level counting without height checks
3. Examine the following buggy code snippet for summing root-to-leaf numbers using DFS recursion. Which line contains the subtle bug causing incorrect sums on some inputs?
class Solution:
    def sumNumbers(self, root: TreeNode) -> int:
        self.total = 0
        def dfs(node, current_number):
            if not node:
                return
            current_number = current_number * 10 + node.val
            self.total += current_number  # Bug here
            if not node.left and not node.right:
                return
            dfs(node.left, current_number)
            dfs(node.right, current_number)
        dfs(root, 0)
        return self.total
medium
A. Line with 'if not node:' return statement
B. Line adding current_number to self.total
C. Line checking if node is leaf
D. Line calling dfs on node.right

Solution

  1. Step 1: Understand when sums should be added

    Sum should only include complete root-to-leaf numbers, so addition must happen only at leaf nodes.
  2. Step 2: Identify incorrect addition

    Adding current_number at every node (including non-leaf) causes partial numbers to be summed, inflating total incorrectly.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Sum only at leaves fixes the bug [OK]
Hint: Add to total only at leaf nodes [OK]
Common Mistakes:
  • Adding partial path sums at non-leaf nodes
  • Not resetting total between calls
4. Suppose you want to perform inorder traversal on a binary tree where nodes can have parent pointers but no left or right child pointers. Which approach correctly produces the inorder sequence without recursion or extra stack?
hard
A. Use Morris traversal by creating threads on parent pointers instead of child pointers.
B. Use breadth-first search since parent pointers allow level order traversal.
C. Use recursive traversal ignoring parent pointers, which will fail due to missing child links.
D. Use iterative traversal with a pointer to the current node and track previously visited node to decide traversal direction.

Solution

  1. Step 1: Understand traversal constraints

    Without left/right child pointers, Morris traversal is not applicable since it relies on child links.
  2. Step 2: Use parent pointers to simulate traversal

    By tracking current and previously visited nodes, we can move up or down the tree to simulate inorder traversal iteratively.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Tracking previous node enables correct traversal without extra space [OK]
Hint: Parent pointers + prev node tracking enable traversal [OK]
Common Mistakes:
  • Trying to create threads on parent pointers
  • Using recursion without child pointers
  • Confusing BFS with inorder traversal
5. Suppose the binary tree nodes have parent pointers already, but the tree is very deep (height h). You want to find the Lowest Common Ancestor of two nodes p and q. Which approach is best to optimize time and space, and why?
hard
A. Use brute force by checking all ancestors of p and q repeatedly until common ancestor is found.
B. Use the parent pointer + ancestor set approach, building a set for p's ancestors and then traversing q's ancestors.
C. Use recursive DFS from root to find paths to p and q, then compare paths to find LCA.
D. Use the parent pointer + two-pointer technique: move the deeper node up until both nodes are at the same depth, then move both up simultaneously until they meet.

Solution

  1. Step 1: Understand problem constraints

    Nodes have parent pointers, tree is deep, so space usage matters.
  2. Step 2: Evaluate approaches

    Ancestor set approach (B) uses O(h) space. Recursive DFS (C) and brute force (D) are inefficient for deep trees.
  3. Step 3: Two-pointer technique

    Moving deeper node up to equalize depth, then moving both up simultaneously uses O(1) space and O(h) time, optimal for deep trees.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Two-pointer approach optimizes space and time for deep trees with parent pointers [OK]
Hint: Two-pointer approach uses O(1) space for LCA with parent pointers [OK]
Common Mistakes:
  • Using ancestor sets unnecessarily
  • Recursing from root wasting time
  • Brute force repeated ancestor checks