Binary Search
The algorithm that finds anything in a sorted list in logarithmic time.
On this page
The phone book problem#
Suppose you need to find a name in a phone book with a million entries. A linear search — checking every name from the start — takes up to a million comparisons in the worst case. Absurd.
But no one searches a phone book linearly. You open to the middle, check whether the name comes before or after, then flip to the middle of the relevant half. You repeat. Within twenty flips, you've found any entry in a million-page book.
This is binary search. It is one of the most fundamental algorithms in computer science — simple to describe, subtle to implement correctly, and remarkable in its efficiency.
The algorithm#
Binary search requires a sorted array and a target value. It maintains two pointers — lo and hi — that bound the region where the target could be:
- Compute
mid = floor((lo + hi) / 2). - If
array[mid] == target, done — returnmid. - If
array[mid] < target, the target must be in the right half: setlo = mid + 1. - If
array[mid] > target, sethi = mid - 1. - Repeat until
lo > hi(target not present).
Choose a target from the dropdown, then watch the lo, mid, and hi pointers move. Grayed-out cells are the half that's been eliminated. The orange cell is the current midpoint being compared. In green: found.
Why it takes O(log n) steps#
Each comparison eliminates at least half the remaining elements. After comparisons, at most elements remain. The search ends when this falls to 1:
So binary search takes at most comparisons. For : at most 20 comparisons. For : at most 30 comparisons. The logarithm grows so slowly that no matter how large the sorted dataset, binary search finishes almost instantly.
The contrast is easiest to feel by watching the two strategies grow side by side. Drag the list size and compare the worst-case work each one does:
The linear line (pink) climbs in lockstep with the data — double the list, double the work. The binary curve (blue) barely lifts off the floor: every doubling of adds just one comparison. That single-comparison-per-doubling is the whole story of logarithmic time, and it's why the gap becomes astronomical at scale — a billion entries costs a linear scan a billion comparisons but costs binary search only thirty.
This is why databases build sorted indexes. Once data is sorted (or a B-tree index is maintained), finding any record costs lookups rather than a full table scan.
The off-by-one minefield#
Jon Bentley famously wrote in Programming Pearls that he surveyed professional programmers on a simple binary search implementation task, and the majority produced buggy code. The bugs almost always involve boundary conditions.
Consider this common mistake: mid = (lo + hi) / 2. In languages without arbitrary-precision integers (C, Java), lo + hi can overflow when both are large. The safe version is mid = lo + (hi - lo) / 2.
Another trap: hi = array.length - 1 vs hi = array.length. The invariant must be consistent throughout. Mixing 0-indexed and 1-indexed thinking inside the same loop produces off-by-one errors that pass most tests but fail on edge cases (empty arrays, single elements, duplicates).
Donald Knuth proved that it took 16 years after binary search was first published before a correct implementation appeared in print. The idea is simple; the implementation is treacherous.
Generalization: binary search on the answer#
Binary search doesn't require an actual array — it works on any monotone predicate. If you can ask "is the answer ≥ x?" and the answer flips from No to Yes at exactly one threshold, you can binary search for that threshold.
This technique appears everywhere:
- Finding square roots: binary search on the answer space for the value where .
- Scheduling problems: "can I complete all tasks within time?" If the answer is monotone in , binary search finds the minimum feasible .
- Database range queries: sorted indexes let databases binary search for the first and last matching row of a range query, then read only the rows between.
This is sometimes called "binary search on the answer space" or "parametric search", and it's a standard tool in algorithm design.
When not to use it#
Binary search requires the data to be sorted. If your data isn't sorted, you either need to sort it first (), or use a different structure like a hash table for lookup.
The trade-off: hash tables give expected lookup but don't support range queries or ordered traversal. A sorted array (or balanced BST) gives lookup but also supports "find all values between A and B" in where is the number of results.
Choosing between them depends on what queries you need. If you only ever ask "is exactly X in the data?", use a hash table. If you ask "what values are between X and Y?", you need sorted order — and binary search.
- Binary search halves the search space every step, so it finds any item in a sorted list of in at most comparisons — 20 for a million, 30 for a billion.
- Every doubling of the data adds just one comparison; that's what logarithmic time feels like.
- The idea is simple but the implementation is a boundary-condition minefield — use
mid = lo + (hi - lo) / 2and keep your loop invariant consistent. - It generalizes far beyond arrays: any monotone predicate that flips from No to Yes once can be binary-searched ("binary search on the answer").
- It needs sorted data. For pure membership tests a hash table is ; choose binary search when you also need order and range queries.
Share this article