Binary Search
7 questions. The most templated section in the 150 — and the one where a single off-by-one silently turns a correct idea into an infinite loop.
The one idea
Most people learn binary search as "search a sorted array". That framing is too narrow, and it's why the harder variants feel like different problems. The real definition:
There is a predicate that is
false, false, …, false, true, true, …, trueover some ordered space, and you want the boundary.
Each step you test the middle. The result tells you which half the boundary is in, so you discard the other half. Halving repeatedly gives log₂ n steps: 20 for a million elements, 30 for a billion.
How the space collapses
Each step throws away half of what remains — so the saving compounds.
The three uses in this section
1. Search an index space — the classic, plus rotated variants. (Q1, Q2, Q4, Q5, Q6)
2. Search an answer space — the array isn't what you search; the candidate answers are. (Q3)
Koko Eating Bananas searches eating speeds rather than array elements: speed 1, 2, 3 are too slow, 4 and above all work. The boundary — the slowest speed that fits — is the answer.
3. Search a partition — you're looking for where to cut, not for a value. (Q7)
The two conventions — pick one and never mix them
Almost every binary search bug is a mismatch here.
| Bounds | Loop condition | Updates | hi starts at |
|---|---|---|---|
Inclusive [lo, hi] | lo <= hi | lo = mid+1, hi = mid-1 | n - 1 |
Half-open [lo, hi) | lo < hi | lo = mid+1, hi = mid | n |
What goes wrong when you mix them:
lo <= hiwithhi = mid→ infinite loop (whenlo == hi,mid == lo, sohi = midchanges nothing)lo < hiwithhi = mid - 1→ the answer can be skipped
The overflow trap
int mid = (lo + hi) / 2; // ✗ overflows when lo + hi > 2^31 - 1
int mid = lo + (hi - lo) / 2; // ✓ only ever computes a differencelo + hi can exceed the int range and wrap negative, indexing outside the array. This exact bug sat in the JDK's own Arrays.binarySearch for nine years — interviewers know the story and notice the safe form.
Prerequisites from Part 1
Debugging checklist
When it loops forever or is off by one, check in this order:
- Do the loop condition and the pointer updates belong to the same convention? This is the cause about 80% of the time.
- Can a pointer fail to move on some branch?
hi = midis safe only withlo < hi, becausemid < hiis then guaranteed. - Is
midcomputed aslo + (hi - lo) / 2? - Is the predicate genuinely monotonic over the search space? If not, binary search is the wrong tool and no amount of index fixing will save it.
How to work this section
- Read §1 and §2 only, then attempt cold with a timer — 20 min Easy, 35 Medium, 45 Hard.
- After the attempt, read §3 and §4 and diff against your reasoning.
- Use the counter-questions in §3 as a quiz — for each approach, cover the answers and defend it yourself.
- Read §7 last: the modified-constraint variants.
Write the convention down before you code. Deciding "inclusive bounds, lo <= hi, mid ± 1" first prevents the entire class of bugs above.
What to carry forward
- The predicate framing — "find the boundary of a monotonic predicate" — which is what makes Q3 and Q7 solvable at all.
- The boundary-search template (first index where a predicate is true), from which
lower_bound,upper_bound, and "count of X" all follow by changing only the predicate. - Reading
10^9in the constraints as "binary search the value range, not the array". - Comparing against
nums[hi], nevernums[lo], in rotated arrays — the single most common bug in that family.