Validate Binary Search Tree
Given the root of a binary tree, return true if it is a valid binary search tree (BST) and false otherwise.
A tree is a valid BST when, for every node:
- every value in its left subtree is strictly smaller than the node's value,
- every value in its right subtree is strictly larger than the node's value, and
- both subtrees are themselves valid BSTs.
Equal values are therefore not allowed anywhere in the tree. The tree is given in level order with null for missing children.
Example 1
Example 2
Example 3
Constraints
The number of nodes is between 1 and 104.-231 <= Node.val <= 231 - 1- The tree height is at most 300.
Pattern Path · Binary Search Trees · problem 100 of 169
Start simple
For every node, scan its entire left and right subtrees and compare all values with it: O(n²) in the worst case (O(n log n) for balanced trees).
The key idea
The BST property is about whole subtrees, not just children. A node deep on the right side of the root must be larger than the root, even if it is the left child of its own parent. So each node has an allowed open interval (low, high) inherited from its ancestors.
Start at the root with no limits. When you step into the left child, everything there must stay below the current value, so the new interval is (low, node.val). Stepping right gives (node.val, high). A node outside its interval breaks the property; if every node lies inside its interval, every ancestor's condition holds, because the interval is exactly the intersection of all the constraints its ancestors impose.
Use "no bound" (None / null or infinities) instead of integer sentinels, because node values may be the smallest or largest 32-bit integers. Each node is visited once, so the check is linear. Equivalent alternative: traverse in order and verify each value is strictly greater than the previous one.
Complexity
- Time:
O(n) - Space:
O(h) recursion depth, h = tree height
Python
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
def valid(node, low, high):
# Every value in this subtree must lie strictly between low and high
# (None means "no limit on this side").
if node is None:
return True
if low is not None and node.val <= low:
return False
if high is not None and node.val >= high:
return False
return valid(node.left, low, node.val) and valid(node.right, node.val, high)
return valid(root, None, None)
JavaScript
var isValidBST = function(root) {
// Every value in this subtree must lie strictly between low and high
// (null means "no limit on this side").
const valid = (node, low, high) => {
if (node === null) return true;
if (low !== null && node.val <= low) return false;
if (high !== null && node.val >= high) return false;
return valid(node.left, low, node.val) && valid(node.right, node.val, high);
};
return valid(root, null, null);
};
What the interviewer is checking
The interviewer wants to see whether you catch the classic trap (checking only parent-child pairs) and can explain why ranges or in-order traversal capture the full definition.
Mistakes to avoid
- Comparing a node only with its direct children.
- Allowing equal values (
<=instead of<). - Using
INT_MIN/INT_MAXas initial bounds, which wrongly rejects trees that contain those exact values. - In the in-order approach, initialising
prevto a number instead of "none yet".
Follow-ups to expect
- Can you do it iteratively? (In-order traversal with an explicit stack.)
- How would you find the two swapped nodes in a broken BST? (In-order, find the inversions.)
- What if duplicates were allowed on one side? (Make that bound inclusive.)
In real software
Databases and in-memory ordered maps keep search trees whose ordering invariant must hold for lookups to be correct; consistency checkers walk the tree with exactly these range bounds.