Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1026. Maximum Difference Between Node and Ancestor - Leetcode Solution

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 maxAncestorDiff(self, root: TreeNode) -> int:
        def helper(node, cur_min, cur_max):
            if not node:
                return cur_max - cur_min
            cur_min = min(cur_min, node.val)
            cur_max = max(cur_max, node.val)
            left = helper(node.left, cur_min, cur_max)
            right = helper(node.right, cur_min, cur_max)
            return max(left, right)
        return helper(root, root.val, root.val)
      
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int helper(TreeNode* node, int cur_min, int cur_max) {
        if (!node) return cur_max - cur_min;
        cur_min = std::min(cur_min, node->val);
        cur_max = std::max(cur_max, node->val);
        int left = helper(node->left, cur_min, cur_max);
        int right = helper(node->right, cur_min, cur_max);
        return std::max(left, right);
    }
    int maxAncestorDiff(TreeNode* root) {
        return helper(root, root->val, root->val);
    }
};
      
/**
 * Definition for a binary tree node.
 * public 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;
 *     }
 * }
 */
class Solution {
    public int maxAncestorDiff(TreeNode root) {
        return helper(root, root.val, root.val);
    }
    private int helper(TreeNode node, int curMin, int curMax) {
        if (node == null) return curMax - curMin;
        curMin = Math.min(curMin, node.val);
        curMax = Math.max(curMax, node.val);
        int left = helper(node.left, curMin, curMax);
        int right = helper(node.right, curMin, curMax);
        return Math.max(left, right);
    }
}
      
/**
 * 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)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxAncestorDiff = function(root) {
    function helper(node, curMin, curMax) {
        if (!node) return curMax - curMin;
        curMin = Math.min(curMin, node.val);
        curMax = Math.max(curMax, node.val);
        let left = helper(node.left, curMin, curMax);
        let right = helper(node.right, curMin, curMax);
        return Math.max(left, right);
    }
    return helper(root, root.val, root.val);
};
      

Problem Description

You are given the root of a binary tree. For each node in the tree, consider the difference between that node's value and the values of all its ancestor nodes. Your task is to find the maximum such difference across the entire tree.

  • The binary tree is made up of nodes, each containing an integer value (val), and pointers to left and right children.
  • For any node, its ancestors are all the nodes along the path from the root to that node (excluding itself).
  • You must return a single integer: the largest absolute difference between the value of any node and the value of any of its ancestor nodes.
  • Constraints:
    • Each node value is an integer.
    • The tree has at least one node.

Thought Process

To solve this problem, we need to compare each node's value with all of its ancestors. The brute-force way would be, for every node, to walk up its ancestor path and compute the difference with each ancestor. However, this would be inefficient because it could lead to repeated computations and a lot of redundant traversals.

Instead, we can notice that as we traverse from the root to any node, we can keep track of the minimum and maximum values seen so far along the path. For any current node, the largest possible difference with its ancestors is the maximum of:

  • The absolute difference between its value and the minimum value seen so far
  • The absolute difference between its value and the maximum value seen so far
By carrying along the minimum and maximum values as we traverse, we can efficiently compute the required difference at every node.

Solution Approach

We will use a Depth-First Search (DFS) traversal of the tree, passing down the minimum and maximum values found along the path from the root to the current node. Here’s how the algorithm works step-by-step:

  1. Start the traversal from the root node. Initialize both the minimum and maximum values with the root's value.
  2. At each node:
    • Update the minimum and maximum values by comparing the current node's value with the running minimum and maximum.
    • Recursively call the function for the left and right children, passing along the updated minimum and maximum.
  3. When you reach a null node (i.e., a leaf's child), return the difference between the maximum and minimum values collected along the path.
  4. For each node, the answer is the maximum of the results from its left and right subtrees.
  5. The global answer is the maximum difference found in any path from the root to a leaf.

This approach ensures that each node is visited only once, and the minimum and maximum values are efficiently propagated down the tree without redundant computations.

Example Walkthrough

Let's consider the following binary tree:

        8
       / \
      3   10
     / \    \
    1   6    14
       / \   /
      4   7 13
  

We start at the root (8), so both min and max are 8.

  • Go left to 3: min = min(8,3) = 3, max = max(8,3) = 8
  • Go left to 1: min = min(3,1) = 1, max = 8; at leaf, difference = 8-1 = 7
  • Back to 3, go right to 6: min = 3, max = 8
  • 6's left child 4: min = 3, max = 8, value = 4; at leaf, difference = 8-3 = 5
  • 6's right child 7: min = 3, max = 8, value = 7; at leaf, difference = 8-3 = 5
  • Back to root, go right to 10: min = 8, max = 10
  • 10's right child 14: min = 8, max = 14
  • 14's left child 13: min = 8, max = 14, value = 13; at leaf, difference = 14-8 = 6

The maximum difference found is 7 (between node 1 and ancestor 8).

Time and Space Complexity

  • Brute-force approach: For each node, walking up its ancestor path could take O(h) time, where h is the tree height. Across all nodes, this could be O(Nh), where N is the number of nodes. In the worst case (skewed tree), h = N, so O(N^2).
  • Optimized DFS approach: Each node is visited once, and at each visit, only constant-time operations are performed (updating min/max, recursive calls). Thus, the time complexity is O(N).
  • Space complexity: The extra space is due to the recursion stack. In the worst case (skewed tree), the stack could be O(N). For a balanced tree, it's O(log N).

Summary

By keeping track of the minimum and maximum values along the path from the root to each node, we can efficiently compute the maximum difference between any node and its ancestors in a single traversal. This approach avoids redundant calculations and leverages the properties of depth-first search to achieve optimal time and space complexity. The key insight is propagating state (min/max) down the tree, so every node knows the range of its ancestors without explicitly storing ancestor lists.