DSA Roadmap/Binary Search

Binary Search Fundamentals

The precise loop invariants and boundary rules that separate a correct O(log n) search from an infinite loop or an off-by-one bug.

!!!2/5Theory: 1h 30m9 problems

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) — comparing nums[mid] to nums[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 length m * n and 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:

TemplateInterval conventionLoop conditionUse case
Exact match[lo, hi] inclusivewhile lo <= hi"Does target exist, and at what index?"
Lower bound (leftmost True)[lo, hi) half-openwhile lo < hi"First index where predicate becomes true" / insertion point
Upper bound (rightmost element <= target)[lo, hi) half-openwhile 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 lo

The 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

BugSymptomFix
Wrong loop condition (< vs <=) mismatched with interval conventionInfinite loop or off-by-one miss at boundaryClosed interval [lo, hi]while lo <= hi. Half-open [lo, hi)while lo < hi. Never mix them.
hi = mid in a closed-interval searchInfinite loop when lo == mid and hi never shrinksIn [lo, hi], rejecting the left half must be hi = mid - 1, not hi = mid.
mid computed as (lo + hi) // 2Integer overflow in fixed-width-int languages (Java, C++) on huge index rangesUse lo + (hi - lo) // 2. Harmless in Python (arbitrary precision), but say it out loud anyway — interviewers listen for this.
Rightmost/leftmost mid roundingInfinite loop specifically when hi == lo + 1For 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)

Practice Problems

Interview relevancy:!!!Critical — must-have!!Important — highly recommended!Good to have~Niche — rarely asked

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.