Binary search, step through it on a sorted array
Binary search finds a value in a sorted array by repeatedly checking the middle element and throwing away the half that cannot contain it. A million elements need at most 20 checks. The idea is simple; getting every boundary right is famously not.
On this page
Think of looking up a word in a paper dictionary. You don’t read from the first page: you open it in the middle, see whether your word comes before or after, and repeat in the half that’s left. Binary search is that strategy made precise. It only works because the data is sorted, which is what lets one comparison rule out half the remaining candidates.
Step through it
The array below is searched for 63. lo and hi mark the range that could still contain the target; mid is the element being probed. Faded cells have been ruled out. Try a target that is not in the array, then switch to the lower-bound variant.
Binary search stepperValues are sorted for you. Try a target that is missing, too.
- lo
- mid (probe)
- hi
- Ruled out
- Answer
Pseudocode
Variables
Probes
Worked example: searching for 63
The array is 3, 8, 15, 21, 27, 34, 42, 50, 57, 63, 71, 79, 86, 94, 99 (indices 0 to 14).
| Probe | lo | hi | mid | A[mid] | Decision |
|---|---|---|---|---|---|
| 1 | 0 | 14 | 7 | 50 | 50 < 63, so lo ← 8 |
| 2 | 8 | 14 | 11 | 79 | 79 > 63, so hi ← 10 |
| 3 | 8 | 10 | 9 | 63 | Found at index 9 |
Three probes instead of the ten a left-to-right scan would need.
Why it is so fast
Each probe either finds the target or at least halves the number of candidates. Starting from candidates, after probes at most remain, so the search ends after at most probes. That is time, against for scanning.
| n | Linear scan, worst case | Binary search, worst case |
|---|---|---|
| 10 | 10 | 4 |
| 100 | 100 | 7 |
| 1,000 | 1,000 | 10 |
| 1,000,000 | 1,000,000 | 20 |
| 1,000,000,000 | 1,000,000,000 | 30 |
The catch is the precondition. Sorting costs , so binary search pays off when you search the same data many times, or when it arrives sorted anyway.
Why it is correct
The loop maintains one invariant: if the target is anywhere in the array, it is in . It holds at the start, when the range is the whole array. Each probe keeps it: if , then because the array is sorted, everything at or left of is also too small, so moving to loses nothing. The other case is symmetric.
The loop also terminates, because shrinks on every iteration. When the range is empty () the invariant says the target is nowhere, so “not found” is the right answer. Invariant plus termination is the standard way to prove a loop correct, and it is the loop version of proof by induction.
Where it goes wrong
Jon Bentley reported that when he asked professional programmers to write binary search, most of their versions had bugs. The usual suspects are:
- Mixing up the boundaries. Decide whether
hiis inclusive (the last candidate) or exclusive (one past it) and stick to it. The classic version on this page is inclusive (while lo ≤ hi,hi ← mid − 1); the lower-bound version is exclusive (while lo < hi,hi ← mid). Mixing the two styles creates infinite loops or skipped elements. - Not making progress. Writing
lo ← midin the inclusive version can loop forever whenloandhiare neighbours, becausemidrounds down tolo. - Overflow. In languages with fixed-size integers,
(lo + hi) / 2overflows when both are large. Writinglo + (hi − lo) / 2avoids it. The JDK’s own binary search had exactly this bug until 2006.
Lower bound: the first position that is at least the target
Often you want more than “is it there?”: where is the first copy of a repeated value, or where would a new value be inserted to keep the array sorted? The lower-bound search answers both. It returns the smallest index with (or if there is none). It never stops early, so it always uses about probes, and it finds the leftmost of several equal values. Python’s bisect_left and C++’s std::lower_bound are this function.
Binary search on the answer
The idea works on any yes/no question whose answer flips exactly once as a number increases. To find the integer square root of , the largest with , search over instead of over an array: the test “?” is true up to the answer and false after it.
Worked example: integer square root of 50
Keep as a value known to pass and as the largest value not yet ruled out, starting from . Because a passing probe sets , round the midpoint up, or the range can stop shrinking.
| lo | hi | mid | mid² | Decision |
|---|---|---|---|---|
| 0 | 50 | 25 | 625 | too big: hi ← 24 |
| 0 | 24 | 12 | 144 | too big: hi ← 11 |
| 0 | 11 | 6 | 36 | fits: lo ← 6 |
| 6 | 11 | 9 | 81 | too big: hi ← 8 |
| 6 | 8 | 7 | 49 | fits: lo ← 7 |
| 7 | 8 | 8 | 64 | too big: hi ← 7 |
Now , and indeed .
git bisect), the smallest capacity that fits a workload, or the largest speed that keeps an error under a threshold are all binary search on the answer.
References
- D. E. Knuth, The Art of Computer Programming, Volume 3: Sorting and Searching, 2nd ed., Addison-Wesley, 1998, §6.2.1.
- J. Bentley, Programming Pearls, 2nd ed., Addison-Wesley, 2000. Column 4 is about writing a correct binary search.
- “Binary search”, Wikipedia.