Intuition
A common bug: "BST = each node bigger than its left child and smaller than its right." That's wrong. A node's value must be greater than every ancestor that has it in its right subtree, and less than every ancestor that has it in its left subtree. Pass an explicit (low, high) bound down and tighten it as you descend.
Explanation
Why local checks aren't enough
Consider this tree:
10
/ \
5 15
/ \
6 20
Node 15 > 10, fine. Node 6 < 15, fine. But 6 is in 10's right subtree, so it must be > 10. The local parent-child check passes; the BST property fails.
Steps
Define dfs(node, lo, hi) returning whether the subtree rooted at node is a valid BST whose values lie strictly inside (lo, hi).
- Base case: if
node is None, returnTrue(an empty subtree is fine). - Bound check: if
node.val <= loornode.val >= hi, returnFalse. - Recurse with tighter bounds:
- The left child must be
< node.val. So calldfs(node.left, lo, node.val). - The right child must be
> node.val. So calldfs(node.right, node.val, hi).
- The left child must be
- Both sides must succeed: return their
and.
Initial call: dfs(root, -∞, +∞).
Walk-through
The buggy tree above:
dfs(10, -∞, +∞)
10 ∈ (-∞, +∞) ✓
dfs(5, -∞, 10)
5 ∈ (-∞, 10) ✓
dfs(None, -∞, 5) → True
dfs(None, 5, 10) → True
return True
dfs(15, 10, +∞)
15 ∈ (10, +∞) ✓
dfs(6, 10, 15)
6 ∈ (10, 15)? 6 ≤ 10 ✗ return False ← caught!
return False
return False
A correct BST traverses without ever hitting the False branch.
Edge cases
- Empty tree: root is
None, returnsTrue. - Single node: any value works;
(lo, hi) = (-∞, +∞). - Duplicate values: the strict
<comparison rejects duplicates. (Some BST variants allow them; the standard problem doesn't.) - Boundary integer values:
float('-inf')andfloat('+inf')are safer thanINT_MIN/INT_MAXwhen a node could legitimately equal those.
Complexity
- Time:
O(n)— every node is visited once. - Space:
O(h)for the recursion stack, wherehis the tree height (O(n)worst case for a skewed tree,O(log n)balanced).
Implementation
import java.util.*;
class Solution {
static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) { this.val = val; }
}
public boolean isValidBST(TreeNode root) {
return dfs(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean dfs(TreeNode node, long lo, long hi) {
if (node == null) return true;
if (node.val <= lo || node.val >= hi) return false;
return dfs(node.left, lo, node.val) && dfs(node.right, node.val, hi);
}
}