Validate Binary Search Tree

MediumTrees · BST · In-order / DFS Bounds🧩 Binary Search Tree & In-orderLeetCode #98 ↗Updated

Problem

Given the root of a binary tree, decide whether it is a valid BST: every value in a node's left subtree is strictly smaller than the node, every value in its right subtree is strictly larger, and both subtrees are themselves valid BSTs.
Inputroot = [2,1,3]Outputtrue
Inputroot = [5,1,4,null,null,3,6]Outputfalse
Inputroot = [5,4,6,null,null,3,7]Outputfalse

1 ≤ n ≤ 104  ·  -231 ≤ Node.val ≤ 231-1  ·  duplicates make it invalid

Try it yourself

Java
class Solution {
    public boolean isValidBST(TreeNode root) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Checking only parent vs child is not enough (example 3: 3 sits under 6 but is less than the root 5). Either compare against an inherited (low, high) window, or use the fact that a BST's in-order sequence is strictly increasing.
PatternBinary Search Tree & In-order

Instinct The tree is a BST? Its in-order walk is a sorted list and every node splits the search space - think "sorted array in tree form", not "visit everything".

Memory hack Left is less, right is more, in-order reads the sorted score - and every node lives inside a (low, high) fence set by its ancestors.

Open the Binary Search Tree & In-order guide →

Approaches

Brute ForceIn-order into a List, then Scan

Time O(n)Space O(n)

Collect all values with a recursive in-order walk. A tree is a BST exactly when that list is strictly increasing, so check each adjacent pair; an equal pair (duplicate) also fails.

Java
public boolean isValidBST(TreeNode root) {
    List<Integer> vals = new ArrayList<>();
    inorder(root, vals);
    for (int i = 1; i < vals.size(); i++) {
        if (vals.get(i) <= vals.get(i - 1)) return false;
    }
    return true;
}

private void inorder(TreeNode node, List<Integer> vals) {
    if (node == null) return;
    inorder(node.left, vals);
    vals.add(node.val);
    inorder(node.right, vals);
}

Simple and correct, but always stores all n values and never stops early.

At a glance

LevelApproachTimeSpace
Brute ForceIn-order into a List, then ScanO(n)O(n)
BetterIterative In-order with prevO(n)O(h)
OptimalDFS with (low, high) BoundsO(n)O(h)
Common pitfalls
  • Only comparing each node with its direct children — misses deeper violations.
  • int bounds with Integer.MIN/MAX_VALUE sentinels — fails on those values.
  • Using < instead of <= — accepts duplicates like [2,2,2].
Interview tips
  • Draw example 3 to show why the local parent-child check is wrong.
  • Present both views: interval bounds and strictly increasing in-order.
  • Ask whether duplicates are allowed; here they are not.
Pattern
Top-down DFS with inherited constraints → validate a property that depends on all ancestors, not just the parent.
My notes saved in this browser