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.
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:
- Perform an inorder traversal of the BST using DFS
- During traversal, keep track of the previous node's value
- Calculate the difference between the current node and the previous node
- 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 gracefullyans
: Keeps track of the minimum difference found so far. Initialized toinf
to ensure any valid difference will be smaller
Algorithm Implementation:
The dfs
function follows the standard inorder traversal pattern:
- Base Case: If the current node is
None
, return immediately - Left Subtree: Recursively traverse the left subtree with
dfs(root.left)
- 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
- Calculate the difference between current node's value and the previous node's value:
- 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 EvaluatorExample 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
- Calculate difference:
- 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
- Calculate difference:
- 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
- Calculate difference:
- 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
- Calculate difference:
- 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
- Calculate difference:
- 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
Which two pointer techniques do you use to check if a string is a palindrome?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Want a Structured Path to Master System Design Too? Don’t Miss This!