Facebook Pixel

783. Minimum Distance Between BST Nodes

Problem Description

You are given the root of a Binary Search Tree (BST). Your task is to find the minimum difference between the values of any two different nodes in the tree.

In other words, you need to look at all possible pairs of nodes in the BST and return the smallest absolute difference between their values.

For example, if you have a BST with nodes containing values [4, 2, 6, 1, 3], the differences between all pairs would include values like |4-2|=2, |4-6|=2, |4-1|=3, |4-3|=1, |2-1|=1, |2-3|=1, |2-6|=4, |6-1|=5, |6-3|=3, |1-3|=2. The minimum among all these differences is 1.

The key insight here is that in a BST, the inorder traversal gives us values in sorted order. When values are sorted, the minimum difference must occur between adjacent elements. Therefore, instead of checking all pairs, we can perform an inorder traversal and keep track of the minimum difference between consecutive nodes in the traversal sequence.

The solution uses a depth-first search (DFS) approach to perform inorder traversal (left → root → right). During traversal, it maintains a variable pre to store the previous node's value and calculates the difference between the current node's value and pre. The variable ans keeps track of the minimum difference found so far.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: A tree is a special type of graph (specifically, a connected acyclic graph). The BST in this problem is represented as a tree structure with nodes and edges (parent-child relationships).

Is it a tree?

  • Yes: The problem explicitly states we're working with a Binary Search Tree (BST), which is a specialized tree data structure where each node has at most two children, and the left subtree contains values less than the node's value while the right subtree contains values greater than the node's value.

DFS

  • Yes: Since we identified this as a tree problem, the flowchart directs us to use DFS (Depth-First Search).

Conclusion: The flowchart suggests using DFS for this tree problem. This aligns perfectly with our solution approach, which uses DFS to perform an inorder traversal of the BST. The inorder traversal is particularly useful here because it visits nodes in ascending order of their values in a BST, allowing us to efficiently find the minimum difference between consecutive values rather than checking all possible pairs.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we need to find the minimum difference between any two nodes in a BST, the naive approach would be to compare every node with every other node, which would take O(n²) time. However, we can leverage the special property of BSTs to optimize this.

The key insight comes from understanding what happens when we perform an inorder traversal on a BST: we get all values in sorted order. For example, if our BST contains values [4, 2, 6, 1, 3], an inorder traversal would give us [1, 2, 3, 4, 6].

Once we have a sorted sequence, finding the minimum difference becomes much simpler. In any sorted array, the minimum difference between any two elements must occur between adjacent elements. Why? Because as elements get farther apart in a sorted sequence, their difference only increases.

Consider the sorted sequence [1, 2, 3, 4, 6]:

  • The difference between adjacent elements: |2-1|=1, |3-2|=1, |4-3|=1, |6-4|=2
  • The minimum is 1

If we tried non-adjacent elements like |3-1|=2 or |6-1|=5, the differences would be larger.

This observation leads us to the solution strategy:

  1. Perform an inorder traversal of the BST using DFS
  2. During traversal, keep track of the previous node's value
  3. Calculate the difference between the current node and the previous node
  4. Keep updating the minimum difference found

By using DFS for inorder traversal and only comparing adjacent values in the sorted sequence, we reduce the problem from O(n²) comparisons to just O(n) comparisons, making it much more efficient.

Learn more about Tree, Depth-First Search, Breadth-First Search, Binary Search Tree and Binary Tree patterns.

Solution Approach

The solution implements an inorder traversal using DFS (Depth-First Search) recursion. During the traversal, we maintain two important variables to track the minimum difference.

Data Structures and Variables:

  • pre: Stores the value of the previously visited node in the inorder traversal. Initialized to -inf to handle the first node gracefully
  • ans: Keeps track of the minimum difference found so far. Initialized to inf to ensure any valid difference will be smaller

Algorithm Implementation:

The dfs function follows the standard inorder traversal pattern:

  1. Base Case: If the current node is None, return immediately
  2. Left Subtree: Recursively traverse the left subtree with dfs(root.left)
  3. Process Current Node:
    • Calculate the difference between current node's value and the previous node's value: root.val - pre
    • Update the minimum difference: ans = min(ans, root.val - pre)
    • Update pre to the current node's value for the next comparison: pre = root.val
  4. Right Subtree: Recursively traverse the right subtree with dfs(root.right)

Why This Works:

Since we're doing an inorder traversal (left → root → right), nodes are visited in ascending order of their values. This means:

  • When we visit a node, pre contains the value of the node with the largest value that's still smaller than the current node
  • The difference root.val - pre gives us the minimum possible difference for the current node with any smaller-valued node
  • By tracking the minimum of all such differences, we find the global minimum difference

Use of nonlocal:

The nonlocal keyword is used to modify the pre and ans variables that are defined in the outer function scope. This allows the recursive dfs function to maintain state across all recursive calls.

Time and Space Complexity:

  • Time: O(n) where n is the number of nodes, as we visit each node exactly once
  • Space: O(h) where h is the height of the tree, due to the recursion call stack

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a small BST example:

       4
      / \
     2   6
    / \
   1   3

We'll trace through the inorder traversal DFS to see how we find the minimum difference.

Initial State:

  • pre = -inf (no previous node visited yet)
  • ans = inf (no minimum found yet)

Step 1: Start DFS from root (4)

  • Call dfs(4)
  • First, traverse left subtree: dfs(2)

Step 2: At node 2

  • Call dfs(2)
  • First, traverse left subtree: dfs(1)

Step 3: At node 1 (leftmost node)

  • Call dfs(1)
  • Left child is None, so continue
  • Process node 1:
    • Calculate difference: 1 - (-inf) = inf
    • Update ans: ans = min(inf, inf) = inf
    • Update pre: pre = 1
  • Right child is None, return

Step 4: Back at node 2

  • Left subtree done, process node 2:
    • Calculate difference: 2 - 1 = 1
    • Update ans: ans = min(inf, 1) = 1
    • Update pre: pre = 2
  • Traverse right subtree: dfs(3)

Step 5: At node 3

  • Call dfs(3)
  • Left child is None
  • Process node 3:
    • Calculate difference: 3 - 2 = 1
    • Update ans: ans = min(1, 1) = 1
    • Update pre: pre = 3
  • Right child is None, return

Step 6: Back at root node 4

  • Left subtree done, process node 4:
    • Calculate difference: 4 - 3 = 1
    • Update ans: ans = min(1, 1) = 1
    • Update pre: pre = 4
  • Traverse right subtree: dfs(6)

Step 7: At node 6

  • Call dfs(6)
  • Left child is None
  • Process node 6:
    • Calculate difference: 6 - 4 = 2
    • Update ans: ans = min(1, 2) = 1
    • Update pre: pre = 6
  • Right child is None, return

Final Result: The minimum difference is 1

The inorder traversal visited nodes in order: [1, 2, 3, 4, 6], and we checked differences between consecutive pairs: (1,2)→1, (2,3)→1, (3,4)→1, (4,6)→2. The minimum is 1.

Solution Implementation

1# Definition for a binary tree node.
2# class TreeNode:
3#     def __init__(self, val=0, left=None, right=None):
4#         self.val = val
5#         self.left = left
6#         self.right = right
7
8from typing import Optional
9from math import inf
10
11class Solution:
12    def minDiffInBST(self, root: Optional[TreeNode]) -> int:
13        """
14        Find the minimum difference between values of any two nodes in a BST.
15      
16        Uses in-order traversal to visit nodes in ascending order,
17        then compares consecutive values to find minimum difference.
18      
19        Args:
20            root: Root node of the binary search tree
21          
22        Returns:
23            Minimum absolute difference between any two node values
24        """
25      
26        def inorder_traversal(node: Optional[TreeNode]) -> None:
27            """
28            Perform in-order traversal of BST (left -> root -> right).
29            Updates minimum difference by comparing consecutive values.
30          
31            Args:
32                node: Current node being visited
33            """
34            if node is None:
35                return
36          
37            # Traverse left subtree first
38            inorder_traversal(node.left)
39          
40            # Process current node - compare with previous value
41            nonlocal previous_value, minimum_difference
42            minimum_difference = min(minimum_difference, node.val - previous_value)
43            previous_value = node.val
44          
45            # Traverse right subtree
46            inorder_traversal(node.right)
47      
48        # Initialize variables
49        previous_value = -inf  # Previous node value in in-order traversal
50        minimum_difference = inf  # Track minimum difference between consecutive nodes
51      
52        # Start in-order traversal from root
53        inorder_traversal(root)
54      
55        return minimum_difference
56
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 *     int val;
5 *     TreeNode left;
6 *     TreeNode right;
7 *     TreeNode() {}
8 *     TreeNode(int val) { this.val = val; }
9 *     TreeNode(int val, TreeNode left, TreeNode right) {
10 *         this.val = val;
11 *         this.left = left;
12 *         this.right = right;
13 *     }
14 * }
15 */
16class Solution {
17    // Constants for representing infinity values
18    private static final int INFINITY = 1 << 30;
19    private static final int NEGATIVE_INFINITY = -INFINITY;
20  
21    // Instance variables to track the minimum difference and previous node value
22    private int minimumDifference;
23    private int previousValue;
24  
25    /**
26     * Finds the minimum difference between values of any two nodes in a BST.
27     * 
28     * @param root The root node of the binary search tree
29     * @return The minimum absolute difference between any two node values
30     */
31    public int minDiffInBST(TreeNode root) {
32        // Initialize the minimum difference to maximum possible value
33        minimumDifference = INFINITY;
34        // Initialize previous value to negative infinity to handle the first node
35        previousValue = NEGATIVE_INFINITY;
36      
37        // Perform in-order traversal to find minimum difference
38        inOrderTraversal(root);
39      
40        return minimumDifference;
41    }
42  
43    /**
44     * Performs an in-order traversal of the BST to find the minimum difference.
45     * In-order traversal visits nodes in ascending order for a BST.
46     * 
47     * @param node The current node being processed
48     */
49    private void inOrderTraversal(TreeNode node) {
50        // Base case: if node is null, return
51        if (node == null) {
52            return;
53        }
54      
55        // Recursively traverse the left subtree
56        inOrderTraversal(node.left);
57      
58        // Process current node: update minimum difference
59        // Since BST is traversed in-order, consecutive values have minimum difference
60        minimumDifference = Math.min(minimumDifference, node.val - previousValue);
61      
62        // Update previous value for next comparison
63        previousValue = node.val;
64      
65        // Recursively traverse the right subtree
66        inOrderTraversal(node.right);
67    }
68}
69
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 *     int val;
5 *     TreeNode *left;
6 *     TreeNode *right;
7 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14    int minDiffInBST(TreeNode* root) {
15        // Initialize minimum difference with a large value
16        const int INF = 1 << 30;
17        int minDifference = INF;
18      
19        // Track the previous node's value during in-order traversal
20        // Initialize with negative infinity to handle the first node
21        int previousValue = -INF;
22      
23        // Define recursive lambda function for in-order traversal
24        // Using C++23 explicit this parameter syntax
25        auto inorderTraversal = [&](this auto&& inorderTraversal, TreeNode* node) -> void {
26            // Base case: if node is null, return
27            if (!node) {
28                return;
29            }
30          
31            // Traverse left subtree first (in-order)
32            inorderTraversal(node->left);
33          
34            // Process current node
35            // Calculate difference between current value and previous value
36            minDifference = min(minDifference, node->val - previousValue);
37          
38            // Update previous value for next comparison
39            previousValue = node->val;
40          
41            // Traverse right subtree
42            inorderTraversal(node->right);
43        };
44      
45        // Start the in-order traversal from root
46        inorderTraversal(root);
47      
48        // Return the minimum difference found
49        return minDifference;
50    }
51};
52
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 *     val: number
5 *     left: TreeNode | null
6 *     right: TreeNode | null
7 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 *         this.val = (val===undefined ? 0 : val)
9 *         this.left = (left===undefined ? null : left)
10 *         this.right = (right===undefined ? null : right)
11 *     }
12 * }
13 */
14
15/**
16 * Finds the minimum difference between any two nodes in a Binary Search Tree
17 * Uses in-order traversal to visit nodes in ascending order
18 * @param root - The root node of the BST
19 * @returns The minimum absolute difference between values of any two nodes
20 */
21function minDiffInBST(root: TreeNode | null): number {
22    // Initialize minimum difference as infinity and previous node value as negative infinity
23    let minDifference: number = Infinity;
24    let previousValue: number = -Infinity;
25  
26    /**
27     * Performs in-order traversal of the BST
28     * In-order traversal visits nodes in ascending order for BST
29     * @param node - Current node being processed
30     */
31    const inOrderTraversal = (node: TreeNode | null): void => {
32        // Base case: if node is null, return
33        if (!node) {
34            return;
35        }
36      
37        // Traverse left subtree first
38        inOrderTraversal(node.left);
39      
40        // Process current node: update minimum difference
41        // Since BST is traversed in-order, previousValue < node.val
42        minDifference = Math.min(minDifference, node.val - previousValue);
43      
44        // Update previous value for next comparison
45        previousValue = node.val;
46      
47        // Traverse right subtree
48        inOrderTraversal(node.right);
49    };
50  
51    // Start the in-order traversal from root
52    inOrderTraversal(root);
53  
54    return minDifference;
55}
56

Time and Space Complexity

Time Complexity: O(n)

The algorithm performs an in-order traversal of the binary search tree using depth-first search (DFS). Each node in the tree is visited exactly once during the traversal. The operations performed at each node (comparison with min() and variable updates) take constant time O(1). Therefore, the overall time complexity is O(n), where n is the total number of nodes in the binary search tree.

Space Complexity: O(n)

The space complexity is determined by the recursive call stack used during the DFS traversal. In the worst case, when the tree is completely unbalanced (essentially a linked list), the recursion depth can reach n levels deep, requiring O(n) space on the call stack. In the best case of a perfectly balanced tree, the recursion depth would be O(log n), but we consider the worst-case scenario for space complexity analysis. Additionally, the algorithm uses only a constant amount of extra space for the variables pre and ans, which doesn't affect the overall space complexity. Therefore, the space complexity is O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow with Initial Values

One common pitfall is using language-specific maximum/minimum integer values instead of infinity. In some programming languages, using Integer.MAX_VALUE or Integer.MIN_VALUE might cause overflow issues when performing arithmetic operations.

Problem Example:

# Potentially problematic in some languages
previous_value = -sys.maxsize  # Could overflow when subtracting
minimum_difference = sys.maxsize

Solution: Use floating-point infinity which handles arithmetic operations safely:

from math import inf
previous_value = -inf
minimum_difference = inf

2. Forgetting to Handle the First Node

A critical pitfall is not properly initializing previous_value. If you initialize it to 0 or any arbitrary value, you might get incorrect results.

Problem Example:

previous_value = 0  # Wrong! This could give incorrect difference for first node
# If first inorder node has value 100, difference would be 100-0=100

Solution: Initialize previous_value to negative infinity so the first comparison is effectively skipped:

previous_value = -inf  # Ensures first node's difference is infinity (ignored)

3. Using Global Variables Instead of Nonlocal

Attempting to modify outer scope variables without the nonlocal keyword will create new local variables instead, causing the function to not work correctly.

Problem Example:

def minDiffInBST(self, root):
    previous_value = -inf
    minimum_difference = inf
  
    def inorder_traversal(node):
        if node is None:
            return
        inorder_traversal(node.left)
        # This creates NEW local variables, doesn't modify outer ones!
        minimum_difference = min(minimum_difference, node.val - previous_value)
        previous_value = node.val
        inorder_traversal(node.right)

Solution: Always declare nonlocal for variables you need to modify:

def inorder_traversal(node):
    if node is None:
        return
    inorder_traversal(node.left)
  
    nonlocal previous_value, minimum_difference  # Essential!
    minimum_difference = min(minimum_difference, node.val - previous_value)
    previous_value = node.val
  
    inorder_traversal(node.right)

4. Comparing All Pairs Instead of Adjacent Values

A conceptual pitfall is trying to compare every node with every other node, leading to O(n²) complexity.

Problem Example:

# Inefficient approach - checking all pairs
def minDiffInBST(self, root):
    values = []
    def collect(node):
        if node:
            values.append(node.val)
            collect(node.left)
            collect(node.right)
  
    collect(root)
    min_diff = inf
    for i in range(len(values)):
        for j in range(i+1, len(values)):
            min_diff = min(min_diff, abs(values[i] - values[j]))
    return min_diff

Solution: Leverage BST property - inorder traversal gives sorted values, so minimum difference must be between adjacent elements:

# Efficient - only check adjacent values in sorted order
def inorder_traversal(node):
    # ... inorder traversal comparing only consecutive values
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which two pointer techniques do you use to check if a string is a palindrome?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More