Validate Binary Search Tree
Imagine you are building a database index that relies on a binary search tree structure. You need to verify if the tree is correctly structured to ensure fast lookups.
Given the root of a binary tree, determine if it is a valid binary search tree (BST). A valid BST is defined as follows: - The left subtree of a node contains only nodes with keys less than the node's key. - The right subtree of a node contains only nodes with keys greater than the node's key. - Both the left and right subtrees must also be binary search trees. Input: root node of a binary tree. Output: true if the tree is a valid BST, false otherwise.
The number of nodes in the tree is in the range [1, 10^5].Node values are integers and can be negative or positive.You must solve the problem with O(n) time complexity."root = [2,1,3]"trueThe tree: 2 / \ 1 3 All left nodes are less than 2 and all right nodes are greater than 2.
"root = [5,1,4,null,null,3,6]"falseThe tree: 5 / \ 1 4 / \ 3 6 Node 4's left child 3 is less than 5 but appears in the right subtree of 5, violating BST rules.
- Single node tree → true
- Tree with duplicate values → false (BST requires strictly less/greater)
- Left skewed tree (all nodes have only left child) → true if values strictly decreasing
- Right skewed tree (all nodes have only right child) → true if values strictly increasing
Fails to detect invalid BSTs where descendants violate BST property
✅ Use min/max range or inorder traversal to check global BST property
Incorrectly returns true for trees with duplicate values violating strict inequality
✅ Use strict less than and greater than comparisons, not less than or equal
Incorrect results when running multiple tests in same program
✅ Reset variables like 'prev' before each test
Fails on edge cases with node values at integer limits
✅ Use long (Java/C++), float('-inf') or -Infinity (Python/JS) to represent bounds
Intuition
For each node, verify that all nodes in its left subtree are less than it and all nodes in its right subtree are greater than it by traversing those subtrees.
Algorithm
- For each node, find the maximum value in its left subtree.
- Find the minimum value in its right subtree.
- Check if the node's value is greater than the max in left subtree and less than the min in right subtree.
- Recursively apply this check to all nodes.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def find_max(node):
if not node:
return float('-inf')
return max(node.val, find_max(node.left), find_max(node.right))
def find_min(node):
if not node:
return float('inf')
return min(node.val, find_min(node.left), find_min(node.right))
def is_valid_bst(root):
if not root:
return True
left_max = find_max(root.left)
right_min = find_min(root.right)
if root.val <= left_max or root.val >= right_min:
return False
return is_valid_bst(root.left) and is_valid_bst(root.right)
# Driver code to test
if __name__ == '__main__':
root = TreeNode(2, TreeNode(1), TreeNode(3))
print(is_valid_bst(root)) # Expected: True
root2 = TreeNode(5, TreeNode(1), TreeNode(4, TreeNode(3), TreeNode(6)))
print(is_valid_bst(root2)) # Expected: Falseclass TreeNode:Define TreeNode class to represent nodes in the binary treedef find_max(node):Helper to find max value in subtree, needed to check BST propertyif not node:Base case returns infinity so empty subtree doesn't affect minreturn max(node.val, find_max(node.left), find_max(node.right))Recursively find max in entire subtreedef find_min(node):Helper to find min value in subtree, needed to check BST propertyreturn min(node.val, find_min(node.left), find_min(node.right))Recursively find min in entire subtreeleft_max = find_max(root.left)Find max in left subtree to compare with current noderight_min = find_min(root.right)Find min in right subtree to compare with current nodeif root.val <= left_max or root.val >= right_min:Check BST property violation at current nodereturn is_valid_bst(root.left) and is_valid_bst(root.right)Recursively validate left and right subtreesclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public int findMax(TreeNode node) {
if (node == null) return Integer.MIN_VALUE;
return Math.max(node.val, Math.max(findMax(node.left), findMax(node.right)));
}
public int findMin(TreeNode node) {
if (node == null) return Integer.MAX_VALUE;
return Math.min(node.val, Math.min(findMin(node.left), findMin(node.right)));
}
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
int leftMax = findMax(root.left);
int rightMin = findMin(root.right);
if (root.val <= leftMax || root.val >= rightMin) return false;
return isValidBST(root.left) && isValidBST(root.right);
}
public static void main(String[] args) {
Solution sol = new Solution();
TreeNode root = new TreeNode(2);
root.left = new TreeNode(1);
root.right = new TreeNode(3);
System.out.println(sol.isValidBST(root)); // true
TreeNode root2 = new TreeNode(5);
root2.left = new TreeNode(1);
root2.right = new TreeNode(4);
root2.right.left = new TreeNode(3);
root2.right.right = new TreeNode(6);
System.out.println(sol.isValidBST(root2)); // false
}
}class TreeNode {Define TreeNode class to represent nodes in the binary treepublic int findMax(TreeNode node) {Helper to find max value in subtree for BST checkif (node == null) return Integer.MIN_VALUE;Base case returns minimal int so empty subtree doesn't affect maxreturn Math.max(node.val, Math.max(findMax(node.left), findMax(node.right)));Recursively find max in entire subtreepublic int findMin(TreeNode node) {Helper to find min value in subtree for BST checkif (node == null) return Integer.MAX_VALUE;Base case returns max int so empty subtree doesn't affect minreturn Math.min(node.val, Math.min(findMin(node.left), findMin(node.right)));Recursively find min in entire subtreeint leftMax = findMax(root.left);Find max in left subtree to compare with current nodeint rightMin = findMin(root.right);Find min in right subtree to compare with current nodeif (root.val <= leftMax || root.val >= rightMin) return false;Check BST property violation at current nodereturn isValidBST(root.left) && isValidBST(root.right);Recursively validate left and right subtrees#include <iostream>
#include <algorithm>
#include <climits>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
int findMax(TreeNode* node) {
if (!node) return INT_MIN;
return max(node->val, max(findMax(node->left), findMax(node->right)));
}
int findMin(TreeNode* node) {
if (!node) return INT_MAX;
return min(node->val, min(findMin(node->left), findMin(node->right)));
}
bool isValidBST(TreeNode* root) {
if (!root) return true;
int leftMax = findMax(root->left);
int rightMin = findMin(root->right);
if (root->val <= leftMax || root->val >= rightMin) return false;
return isValidBST(root->left) && isValidBST(root->right);
}
int main() {
TreeNode* root = new TreeNode(2);
root->left = new TreeNode(1);
root->right = new TreeNode(3);
cout << boolalpha << isValidBST(root) << endl; // true
TreeNode* root2 = new TreeNode(5);
root2->left = new TreeNode(1);
root2->right = new TreeNode(4);
root2->right->left = new TreeNode(3);
root2->right->right = new TreeNode(6);
cout << boolalpha << isValidBST(root2) << endl; // false
return 0;
}struct TreeNode {Define TreeNode struct to represent nodes in the binary treeint findMax(TreeNode* node) {Helper to find max value in subtree for BST validationif (!node) return INT_MIN;Base case returns minimal int so empty subtree doesn't affect maxreturn max(node->val, max(findMax(node->left), findMax(node->right)));Recursively find max in entire subtreeint findMin(TreeNode* node) {Helper to find min value in subtree for BST validationif (!node) return INT_MAX;Base case returns max int so empty subtree doesn't affect minreturn min(node->val, min(findMin(node->left), findMin(node->right)));Recursively find min in entire subtreeint leftMax = findMax(root->left);Find max in left subtree to compare with current nodeint rightMin = findMin(root->right);Find min in right subtree to compare with current nodeif (root->val <= leftMax || root->val >= rightMin) return false;Check BST property violation at current nodereturn isValidBST(root->left) && isValidBST(root->right);Recursively validate left and right subtreesclass TreeNode {
constructor(val=0, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function findMax(node) {
if (!node) return -Infinity;
return Math.max(node.val, findMax(node.left), findMax(node.right));
}
function findMin(node) {
if (!node) return Infinity;
return Math.min(node.val, findMin(node.left), findMin(node.right));
}
function isValidBST(root) {
if (!root) return true;
const leftMax = findMax(root.left);
const rightMin = findMin(root.right);
if (root.val <= leftMax || root.val >= rightMin) return false;
return isValidBST(root.left) && isValidBST(root.right);
}
// Test cases
const root = new TreeNode(2, new TreeNode(1), new TreeNode(3));
console.log(isValidBST(root)); // true
const root2 = new TreeNode(5, new TreeNode(1), new TreeNode(4, new TreeNode(3), new TreeNode(6)));
console.log(isValidBST(root2)); // falseclass TreeNode {Define TreeNode class to represent nodes in the binary treefunction findMax(node) {Helper to find max value in subtree for BST checkif (!node) return -Infinity;Base case returns minimal number so empty subtree doesn't affect maxreturn Math.max(node.val, findMax(node.left), findMax(node.right));Recursively find max in entire subtreefunction findMin(node) {Helper to find min value in subtree for BST checkif (!node) return Infinity;Base case returns max number so empty subtree doesn't affect minreturn Math.min(node.val, findMin(node.left), findMin(node.right));Recursively find min in entire subtreeconst leftMax = findMax(root.left);Find max in left subtree to compare with current nodeconst rightMin = findMin(root.right);Find min in right subtree to compare with current nodeif (root.val <= leftMax || root.val >= rightMin) return false;Check BST property violation at current nodereturn isValidBST(root.left) && isValidBST(root.right);Recursively validate left and right subtreesO(n^2)O(n) due to recursion stack and subtree traversalsFor each node, we traverse its entire left and right subtrees to find min and max, leading to repeated work and quadratic time.
This approach is too slow for large inputs but is useful to understand the BST property deeply before optimizing.
Intuition
Each node must lie within a valid range defined by its ancestors. We recursively check if node values respect these min and max bounds.
Algorithm
- Start with the root node and an initial range (-infinity, +infinity).
- Check if the current node's value lies within the allowed range.
- Recursively validate the left subtree with updated max bound as current node's value.
- Recursively validate the right subtree with updated min bound as current node's value.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def is_valid_bst(root):
def helper(node, low, high):
if not node:
return True
if node.val <= low or node.val >= high:
return False
return helper(node.left, low, node.val) and helper(node.right, node.val, high)
return helper(root, float('-inf'), float('inf'))
# Driver code
if __name__ == '__main__':
root = TreeNode(2, TreeNode(1), TreeNode(3))
print(is_valid_bst(root)) # True
root2 = TreeNode(5, TreeNode(1), TreeNode(4, TreeNode(3), TreeNode(6)))
print(is_valid_bst(root2)) # Falseclass TreeNode:Define TreeNode class to represent nodes in the binary treedef is_valid_bst(root):Main function to validate BST using helperdef helper(node, low, high):Helper function carries current valid range for node valuesif not node:Base case: empty subtree is validif node.val <= low or node.val >= high:Check if current node violates BST range constraintsreturn helper(node.left, low, node.val) and helper(node.right, node.val, high)Recursively validate left and right subtrees with updated rangesreturn helper(root, float('-inf'), float('inf'))Start recursion with full rangeclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public boolean isValidBST(TreeNode root) {
return helper(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean helper(TreeNode node, long low, long high) {
if (node == null) return true;
if (node.val <= low || node.val >= high) return false;
return helper(node.left, low, node.val) && helper(node.right, node.val, high);
}
public static void main(String[] args) {
Solution sol = new Solution();
TreeNode root = new TreeNode(2);
root.left = new TreeNode(1);
root.right = new TreeNode(3);
System.out.println(sol.isValidBST(root)); // true
TreeNode root2 = new TreeNode(5);
root2.left = new TreeNode(1);
root2.right = new TreeNode(4);
root2.right.left = new TreeNode(3);
root2.right.right = new TreeNode(6);
System.out.println(sol.isValidBST(root2)); // false
}
}class TreeNode {Define TreeNode class to represent nodes in the binary treepublic boolean isValidBST(TreeNode root) {Entry point calls helper with full integer rangeprivate boolean helper(TreeNode node, long low, long high) {Helper function with range constraints passed as long to avoid overflowif (node == null) return true;Empty subtree is valid BSTif (node.val <= low || node.val >= high) return false;Check if node violates allowed rangereturn helper(node.left, low, node.val) && helper(node.right, node.val, high);Recurse with updated bounds for left and right subtrees#include <iostream>
#include <climits>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
bool helper(TreeNode* node, long low, long high) {
if (!node) return true;
if (node->val <= low || node->val >= high) return false;
return helper(node->left, low, node->val) && helper(node->right, node->val, high);
}
bool isValidBST(TreeNode* root) {
return helper(root, LONG_MIN, LONG_MAX);
}
int main() {
TreeNode* root = new TreeNode(2);
root->left = new TreeNode(1);
root->right = new TreeNode(3);
cout << boolalpha << isValidBST(root) << endl; // true
TreeNode* root2 = new TreeNode(5);
root2->left = new TreeNode(1);
root2->right = new TreeNode(4);
root2->right->left = new TreeNode(3);
root2->right->right = new TreeNode(6);
cout << boolalpha << isValidBST(root2) << endl; // false
return 0;
}struct TreeNode {Define TreeNode struct to represent nodes in the binary treebool helper(TreeNode* node, long low, long high) {Helper function with range constraints passed as long to avoid overflowif (!node) return true;Empty subtree is valid BSTif (node->val <= low || node->val >= high) return false;Check if node violates BST property with current boundsreturn helper(node->left, low, node->val) && helper(node->right, node->val, high);Recurse with updated bounds for left and right subtreesclass TreeNode {
constructor(val=0, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function isValidBST(root) {
function helper(node, low, high) {
if (!node) return true;
if (node.val <= low || node.val >= high) return false;
return helper(node.left, low, node.val) && helper(node.right, node.val, high);
}
return helper(root, -Infinity, Infinity);
}
// Test cases
const root = new TreeNode(2, new TreeNode(1), new TreeNode(3));
console.log(isValidBST(root)); // true
const root2 = new TreeNode(5, new TreeNode(1), new TreeNode(4, new TreeNode(3), new TreeNode(6)));
console.log(isValidBST(root2)); // falseclass TreeNode {Define TreeNode class to represent nodes in the binary treefunction isValidBST(root) {Main function to validate BST using helperfunction helper(node, low, high) {Helper function carries current valid range for node valuesif (!node) return true;Empty subtree is valid BSTif (node.val <= low || node.val >= high) return false;Check if node violates BST property with current boundsreturn helper(node.left, low, node.val) && helper(node.right, node.val, high);Recurse with updated bounds for left and right subtreesO(n)O(h) where h is tree height due to recursion stackEach node is visited once, and range checks are O(1), so linear time. Space depends on recursion depth.
This approach is efficient and commonly accepted in interviews.
Intuition
Perform an inorder traversal and check if the values are strictly increasing. If not, the tree is not a BST.
Algorithm
- Initialize a variable to track the previous node's value during traversal.
- Perform an inorder traversal (left, node, right).
- At each node, check if current node's value is greater than the previous value.
- If any node violates this, return false; otherwise, return true after traversal.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def is_valid_bst(root):
prev = [float('-inf')]
def inorder(node):
if not node:
return True
if not inorder(node.left):
return False
if node.val <= prev[0]:
return False
prev[0] = node.val
return inorder(node.right)
return inorder(root)
# Driver code
if __name__ == '__main__':
root = TreeNode(2, TreeNode(1), TreeNode(3))
print(is_valid_bst(root)) # True
root2 = TreeNode(5, TreeNode(1), TreeNode(4, TreeNode(3), TreeNode(6)))
print(is_valid_bst(root2)) # Falseclass TreeNode:Define TreeNode class to represent nodes in the binary treeprev = [float('-inf')]Use list to hold previous value so it is mutable in nested functiondef inorder(node):Inorder traversal helper functionif not node:Base case: empty subtree is validif not inorder(node.left):Recursively traverse left subtree and check validityif node.val <= prev[0]:Check if current node violates ascending order propertyprev[0] = node.valUpdate previous value after visiting current nodereturn inorder(node.right)Recursively traverse right subtree and check validityclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
private long prev = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
if (!isValidBST(root.left)) return false;
if (root.val <= prev) return false;
prev = root.val;
return isValidBST(root.right);
}
public static void main(String[] args) {
Solution sol = new Solution();
TreeNode root = new TreeNode(2);
root.left = new TreeNode(1);
root.right = new TreeNode(3);
System.out.println(sol.isValidBST(root)); // true
TreeNode root2 = new TreeNode(5);
root2.left = new TreeNode(1);
root2.right = new TreeNode(4);
root2.right.left = new TreeNode(3);
root2.right.right = new TreeNode(6);
System.out.println(sol.isValidBST(root2)); // false
}
}class TreeNode {Define TreeNode class to represent nodes in the binary treeprivate long prev = Long.MIN_VALUE;Track previous node value globally for inorder traversalpublic boolean isValidBST(TreeNode root) {Main function to validate BST using inorder traversalif (root == null) return true;Empty subtree is valid BSTif (!isValidBST(root.left)) return false;Traverse left subtree firstif (root.val <= prev) return false;Check if current node violates ascending orderprev = root.val;Update previous value after visiting current nodereturn isValidBST(root.right);Traverse right subtree#include <iostream>
#include <climits>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
long prevVal = LONG_MIN;
bool isValidBST(TreeNode* root) {
if (!root) return true;
if (!isValidBST(root->left)) return false;
if (root->val <= prevVal) return false;
prevVal = root->val;
return isValidBST(root->right);
}
int main() {
TreeNode* root = new TreeNode(2);
root->left = new TreeNode(1);
root->right = new TreeNode(3);
cout << boolalpha << isValidBST(root) << endl; // true
prevVal = LONG_MIN; // reset before next test
TreeNode* root2 = new TreeNode(5);
root2->left = new TreeNode(1);
root2->right = new TreeNode(4);
root2->right->left = new TreeNode(3);
root2->right->right = new TreeNode(6);
cout << boolalpha << isValidBST(root2) << endl; // false
return 0;
}struct TreeNode {Define TreeNode struct to represent nodes in the binary treelong prevVal = LONG_MIN;Global variable to track previous node value during inorder traversalbool isValidBST(TreeNode* root) {Main function to validate BST using inorder traversalif (!root) return true;Empty subtree is valid BSTif (!isValidBST(root->left)) return false;Traverse left subtree firstif (root->val <= prevVal) return false;Check if current node violates ascending orderprevVal = root->val;Update previous value after visiting current nodereturn isValidBST(root->right);Traverse right subtreeclass TreeNode {
constructor(val=0, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function isValidBST(root) {
let prev = -Infinity;
function inorder(node) {
if (!node) return true;
if (!inorder(node.left)) return false;
if (node.val <= prev) return false;
prev = node.val;
return inorder(node.right);
}
return inorder(root);
}
// Test cases
const root = new TreeNode(2, new TreeNode(1), new TreeNode(3));
console.log(isValidBST(root)); // true
const root2 = new TreeNode(5, new TreeNode(1), new TreeNode(4, new TreeNode(3), new TreeNode(6)));
console.log(isValidBST(root2)); // falseclass TreeNode {Define TreeNode class to represent nodes in the binary treelet prev = -Infinity;Track previous node value during inorder traversalfunction inorder(node) {Inorder traversal helper functionif (!node) return true;Empty subtree is valid BSTif (!inorder(node.left)) return false;Traverse left subtree firstif (node.val <= prev) return false;Check if current node violates ascending orderprev = node.val;Update previous value after visiting current nodereturn inorder(node.right);Traverse right subtreeO(n)O(h) due to recursion stack, h = tree heightEach node is visited once in inorder traversal, and checks are O(1). Space depends on recursion depth.
This is the most elegant and commonly preferred approach in interviews due to simplicity and efficiency.
| Approach | Time | Space | Stack Risk | Reconstruct | Use In Interview |
|---|---|---|---|---|---|
| 1. Brute Force | O(n^2) | O(n) | Yes (deep recursion) | N/A | Mention only - never code |
| 2. Top-Down Range Validation | O(n) | O(h) | Yes (deep recursion) | N/A | Good to explain and code if comfortable |
| 3. Inorder Traversal Check | O(n) | O(h) | Yes (deep recursion) | N/A | Best approach to code in 95% of interviews |
How to Present
Step 1: Clarify the BST definition and constraints.Step 2: Describe the brute force approach to show understanding.Step 3: Explain the top-down range validation approach for efficiency.Step 4: Present the inorder traversal approach as the optimal solution.Step 5: Code the chosen approach and test with examples and edge cases.
Time Allocation
What the Interviewer Tests
The interviewer tests your understanding of BST properties, ability to optimize naive solutions, and correctness in handling edge cases like duplicates and skewed trees.
Common Follow-ups
- What if duplicates are allowed on the left or right? → Adjust comparison operators accordingly.
- Can you do this iteratively? → Use iterative inorder traversal with a stack.
When to Use
1) Problem involves verifying BST property; 2) Input is a binary tree; 3) Need to check ordering constraints; 4) Output is boolean validity.
