Search and Validate
What is this?
These problems ask you to find a value in a BST or check that a tree is a valid BST at all. The trick is that you never wander the whole tree โ at each node you compare, then step left or right, throwing away the side that cannot hold your answer. It is the same "higher or lower?" guessing game that makes searching feel almost instant.
๐ก Fun fact: The most common BST validation bug is checking each node only against its direct children. A node can look fine next to its parent yet still break the order rule against a far-away ancestor โ so you must pass down a running low-high window.
๐ The 4 problems in this chapter are free. Sign in with Google or Microsoft to start solving.
Core idea: A binary search tree stores a global ordering locally โ everything in the left subtree is smaller, everything in the right subtree is larger. That single invariant lets you make a decision at each node and discard the half you don't need, turning a tree walk into a directed descent. Every problem here is the same move dressed differently: compare against the node, then commit to one side (or prune it entirely).
The pattern
The BST invariant means each node splits the remaining keys into a smaller-left and larger-right block. So instead of exploring the whole tree, you branch on a comparison: too big means go left, too small means go right. That is an O(h) descent โ O(log n) on a balanced tree, O(n) in the worst case when it degenerates into a chain.
Two views of the same invariant power this chapter. The bounds view carries a (low, high) window down each path and asks whether every node stays inside it. The inorder view observes that visiting left, then node, then right yields the keys in strictly increasing order. Whenever you need to validate, prune, or step toward a target, reach for one of these two lenses.
The problems
- Validate Binary Search Tree โ the definitional check. Either confirm the inorder walk is strictly sorted, or push a
(low, high)bounds window down each path and verify every node fits. The trap is checking only a node against its immediate children instead of against the inherited window. - Closest Binary Search Tree Value โ walk toward the target: at each node compare to the target, track the best-so-far, and descend left or right. The ordering guarantees the closest value lies along the single root-to-leaf path you take.
- Range Sum of BST โ sum the keys inside
[low, high]while pruning by bounds: if the node is belowlow, skip its left subtree entirely; if abovehigh, skip its right. The invariant tells you whole subtrees cannot contribute. - First Bad Version โ note this is binary search on a monotonic predicate, not a true BST. There is no tree; you bisect a sorted version range where "bad" flips from false to true exactly once. It lives here because it shares the same halve-and-commit instinct.
Key takeaways
- The invariant is the algorithm: smaller-left, larger-right lets you discard half the keys at every step.
- Two lenses cover everything here โ a
(low, high)bounds window, or the strictly increasing inorder sequence. - Validate against the inherited window, not just the immediate children โ the classic BST validation bug.
- Prune whole subtrees when an entire range falls outside your target interval.
- First Bad Version is binary search, not a BST โ same halving instinct, no tree involved.
- Cost is O(h): O(log n) when balanced, O(n) when the tree degenerates into a chain.
Order: Validate Binary Search Tree โ Closest Binary Search Tree Value โ Range Sum of BST โ First Bad Version.
First Bad Version
Core idea: The "bad?" answer flips from
falsetotrueexactly once and never flips back โ so you're hunting a boundary, and binary search finds a boundary in O(log n) probes instead of O(n).
Problem, rephrased
You ship a CLI tool. Builds are numbered 1, 2, โฆ, n, each one built on top of the previous. At some point a regression slipped in, and every build from that point onward is broken. Builds before it are fine.
You have one tool to check a build: is_broken(v) runs an expensive end-to-end test suite and returns True if build v is broken, False otherwise. Each call costs real minutes of CI time, so you want to make as few calls as possible.
Find the first broken build number.
The crucial structural fact: the answer to is_broken(v) is monotonic. As v increases it goes False, False, โฆ, False, True, True, โฆ, True and never goes back. There is a single dividing line.
build: 1 2 3 4 5 6 7
is_broken: false false false false true true true
^
first bad version = 4
Inputs / outputs
n |
first bad build | is_broken results 1..n |
answer |
|---|---|---|---|
| 7 | 4 | F F F T T T T | 4 |
| 5 | 5 | F F F F T | 5 |
| 6 | 1 | T T T T T T | 1 |
| 1 | 1 | T | 1 |
You return the smallest v for which is_broken(v) is True.
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson