Why interviewers keep asking this
Binary search is the only classic algorithm most candidates believe they already know cold — and it's the one that produces the most live-interview bugs. The idea (halve the search space each step) takes ten seconds to explain. Writing a version that terminates correctly on every input, including size-0, size-1, and size-2 arrays, is a different skill entirely. A Senior-level bar isn't "can you state the algorithm" — it's "can you write it bug-free in one pass while narrating the invariant out loud."
Recognizing the pattern
Binary search applies whenever you can split a search space into two halves and use O(1) or cheap work to determine which half can be discarded. The textbook trigger is a sorted array, but the real trigger is weaker and more general: a monotonic predicate over the index space — a boolean function f(i) that is False for a prefix of indices and True for the rest (or vice versa), with no oscillation in between.
That reframing is what lets binary search apply to structures that aren't obviously "sorted":
- A rotated sorted array (
Search in Rotated Sorted Array) — not globally sorted, but at every index one of the two halves is sorted, which is enough to decide which half to keep. - A unimodal array that strictly increases then strictly decreases (
Find Peak Element) — comparingnums[mid]tonums[mid + 1]gives a monotonic "am I still on the uphill side?" predicate. - A 2D matrix sorted row-by-row and column-by-column (
Search a 2D Matrix) — treat it as a single sorted sequence of lengthm * nand binary search over a virtual 1-D index.
If you can articulate the predicate in one sentence, you can binary search on it. This same reframing is the entire idea behind the next subtopic, Binary Search on the Answer Space — the only difference there is that the search space is a range of candidate answers instead of array indices.
The loop invariant, precisely
Every correct binary search maintains an invariant of the form: "if the answer exists, it is currently within [lo, hi]." Every loop iteration must narrow this range while never accidentally excluding a valid answer. Three canonical templates cover essentially all interview variants:
| Template | Interval convention | Loop condition | Use case |
|---|---|---|---|
| Exact match | [lo, hi] inclusive | while lo <= hi | "Does target exist, and at what index?" |
Lower bound (leftmost True) | [lo, hi) half-open | while lo < hi | "First index where predicate becomes true" / insertion point |
Upper bound (rightmost element <= target) | [lo, hi) half-open | while lo < hi | "Last index where predicate is still true" |
# Template 1: exact match on a sorted array (closed interval [lo, hi])
def binary_search(nums, target):
lo, hi = 0, len(nums) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2 # avoids overflow in languages with fixed-width ints
if nums[mid] == target:
return mid
elif nums[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
# Template 2: lower_bound — first index i such that condition(i) is True
def lower_bound(lo, hi, condition):
# invariant: condition(hi) is always True (or hi is a sentinel "out of range")
while lo < hi:
mid = lo + (hi - lo) // 2
if condition(mid):
hi = mid # mid might be the answer — keep it in range
else:
lo = mid + 1 # mid is definitely not the answer — exclude it
return loThe lower-bound template generalizes cleanly: an upper-bound search is just lower_bound applied to the negated/mirrored predicate, and Python's bisect_left / bisect_right are production implementations of exactly this template.
Complexity
- Time: O(log n) — each iteration halves the search space, so at most ⌈log₂(n + 1)⌉ iterations run.
- Space: O(1) for the iterative form. A recursive implementation is O(log n) space due to call-stack depth — a common "can you avoid the extra space?" follow-up.
If you additionally do O(n) work per iteration (e.g., counting matrix entries <= mid), the total becomes O(n log n) or O(n log k) depending on what k bounds — always state this compound complexity explicitly rather than reflexively saying "O(log n)."
The four classic bugs
| Bug | Symptom | Fix |
|---|---|---|
Wrong loop condition (< vs <=) mismatched with interval convention | Infinite loop or off-by-one miss at boundary | Closed interval [lo, hi] → while lo <= hi. Half-open [lo, hi) → while lo < hi. Never mix them. |
hi = mid in a closed-interval search | Infinite loop when lo == mid and hi never shrinks | In [lo, hi], rejecting the left half must be hi = mid - 1, not hi = mid. |
mid computed as (lo + hi) // 2 | Integer overflow in fixed-width-int languages (Java, C++) on huge index ranges | Use lo + (hi - lo) // 2. Harmless in Python (arbitrary precision), but say it out loud anyway — interviewers listen for this. |
| Rightmost/leftmost mid rounding | Infinite loop specifically when hi == lo + 1 | For a "keep the right candidate" search (lo = mid), round mid up: mid = lo + (hi - lo + 1) // 2, otherwise mid can equal lo forever. |
The single highest-value habit: before coding, say the invariant out loud — "lo and hi are both valid candidate answers at all times" or "the answer is always in [lo, hi)" — and check that every branch preserves it. Interviewers at a Senior bar are explicitly listening for this narration, not just correct code.
Edge cases to test out loud
- Empty array (
n == 0). - Single-element array, target present and absent.
- Target smaller than every element / larger than every element (insertion at the boundaries).
- Duplicate values, when the problem asks for the first or last occurrence specifically (this is precisely what the lower-bound/upper-bound distinction is for).
Where this goes next
The "sorted array, find a value" version above is the easy half of this subtopic. The harder, more distinctive interview skill — extending a monotonic predicate to structures that don't look sorted at all (rotated arrays, unimodal arrays), and then to abstract answer spaces with no array in sight — is what the rest of this topic and the next subtopic build on. You'll also meet this idea again inside the Trees topic, where a Binary Search Tree is essentially binary search materialized as a data structure instead of an algorithm over an array.
Further Resources (Optional)
- GeeksforGeeks — Binary Search AlgorithmArticle10m
- NeetCode — Binary Search (LeetCode 704) walkthroughVideo10m
- USACO Guide — Binary Search on a Sorted ArrayArticle20m
- labuladong — Binary Search Algorithm Code TemplateArticle20m
- Google Research Blog — Nearly All Binary Searches and Mergesorts are Broken (Joshua Bloch)Article10m
- Wikipedia — Binary Search Algorithm (formal analysis and variants)Reference20m
- David Galles (USF) — Interactive Binary and Linear Search VisualizationReference10m
- Abdul Bari — Binary Search (Iterative Method)Video20m
- Book: The Algorithm Design Manual (Skiena, 3rd ed.) — Ch. 4 "Sorting and Searching" (pp. 103-144)Book30m
Practice Problems
Interview relevancy:!!!Critical — must-have!!Important — highly recommended!Good to have~Niche — rarely asked
- Binary SearchEasy!!!1/515m
- Search Insert PositionEasy!!1/515m
- First Bad VersionEasy!!1/515m
- Find First and Last Position of Element in Sorted ArrayMedium!!!2/525m
- Search a 2D MatrixMedium!!!2/525m
- Time Based Key-Value StoreMedium!!2/525m
- Find Peak ElementMedium!!3/530m
- Search in Rotated Sorted ArrayMedium!!!3/530m
- Find Minimum in Rotated Sorted ArrayMedium!!!3/525m
Optional Practice (Extra Reps)
For once you've cleared the main set above and want more reps on this pattern. These don't count toward the roadmap's progress stats — solve them purely for your own benefit.
- Median of Two Sorted ArraysHard~5/550m
- Search in Rotated Sorted Array IIMedium!3/530m
- Single Element in a Sorted ArrayMedium!3/525m