Given the root of a binary tree, return true if it is a valid binary search tree — for every node, all values in its left subtree are strictly smaller and all values in its right subtree are strictly larger.
The tree is given in level-order (breadth-first), using null for missing children.
Input: root = [2, 1, 3] Output: true 1 < 2 < 3 — a valid BST.
Input: root = [5, 1, 4, null, null, 3, 6] Output: false 4 is in the right subtree of 5 but 4 < 5, violating the BST property.
- The number of nodes is in the range [0, 10^4] - -2^31 <= Node.val <= 2^31 - 1
The trap here is checking only a node against its immediate children — that's not enough. The invariant is about entire subtrees: a node deep in the left subtree must still be smaller than a distant ancestor. Two clean ways to enforce it: an inorder traversal must come out strictly increasing, or pass a (low, high) range down and require every node to fall strictly inside it.
“Strict or non-strict?”
Strict — duplicate values make it invalid.
“Is it enough to compare a node to its children?”
No — a node must be valid relative to all ancestors, not just its parent.
The BST property is about whole subtrees, so I either check that the inorder traversal is strictly increasing, or carry a valid (low, high) range down to each node.
Comparing a node only to its direct children is the classic mistake — it misses violations across subtrees.
Worked example — [5, 1, 4, null, null, 3, 6]
5
/ \
1 4 <- 4 is in 5's RIGHT subtree but 4 < 5 -> INVALID
/ \
3 6
inorder: 1, 5, 3, 4, 6 -> 5 then 3 breaks increasing -> false
Every node must respect the bounds set by all its ancestors.
Equivalent check: the inorder sequence has no equal-or-decreasing step.
Left child inherits the parent as an upper bound; right child inherits it as a lower bound.
| Inorder must increase | (low, high) bounds | |
|---|---|---|
| Idea | Inorder walk; each value > previous | Each node strictly within an inherited range |
| Time | O(n) | O(n) |
| Space | O(height) | O(height) |
Both are O(n); pick whichever reads clearer to you. Full code is in the Approaches selector below.
Key takeaway
Validate by whole-subtree bounds — carry a strict (low, high) range down and require low < val < high — or by checking the inorder traversal is strictly increasing. O(n).
valid(node, lo, hi):
if node is null: return true
if not (lo < node.val < hi): return false
return valid(left, lo, node.val) and valid(right, node.val, hi)