Guessing a number, halving each time
Binary Search
The analogy
I picked a number between 1 and 100. You guess 50; I say "higher". Half the numbers just vanished. Guess 75, "lower" โ half again. Seven guesses gets you any number out of a hundred, but only because the numbers are in order.
Visualizer
Watch the range halve
step 1 / 10LO
1
02
13
24
35
47
58
6HI
9
7Sorted first โ without that, none of this is allowed. Hunting for 37.8 values, target 37
๐ In depth โ the full reference
Binary search โ everything worth knowing
| fact | value | note |
|---|---|---|
| time | O(log n) | a billion items in 30 probes; each probe kills half |
| requirement | SORTED data + O(1) random access | a sorted linked list is useless โ no O(1) jump to the middle |
| loop invariant | target, if present, is inside [lo, hi] | every correct variant defends exactly this |
| classic bug #1 | lo < hi vs lo <= hi | off-by-one: <= is right for the closed-interval version |
| classic bug #2 | mid = (lo+hi)/2 overflow | a REAL bug in Java's stdlib for 9 years; Python ints can't overflow, JS is safe below 2^53 |
| variants | first/last occurrence, insertion point | keep searching after a hit โ "bisect_left vs bisect_right" |
| generalisation | binary search on the ANSWER | any monotonic yes/no question: "smallest capacity that ships in D days" โ Module 7 territory |
Python: bisect, the stdlib binary search
import bisect xs = [3, 7, 11, 18, 24, 31, 42] i = bisect.bisect_left(xs, 18) # 3 โ leftmost insertion point found = i < len(xs) and xs[i] == 18 # membership test, O(log n) # bisect_left vs bisect_right on duplicates: ds = [1, 3, 3, 3, 9] print(bisect.bisect_left(ds, 3)) # 1 โ before the run of 3s print(bisect.bisect_right(ds, 3)) # 4 โ after the run # right - left == how many 3s. Grade lookup, timestamp windows, # "closest value" โ all bisect one-liners.
def binary_search(a, t):
lo, hi = 0, len(a) - 1
while lo <= hi:
mid = (lo + hi) // 2
if a[mid] == t:
return mid
if a[mid] < t:
lo = mid + 1 # left half impossible
else:
hi = mid - 1 # right half impossible
return -1
import bisect # the stdlib version
bisect.bisect_left(a, t)Check yourself
Binary search on an unsorted arrayโฆ
Practice โ write it yourself
binary_search(sorted_arr, t): lo/hi bounds, probe the middle with (lo + hi) // 2, discard half each step. Return the index or -1. Watch the boundaries!
Python 3 ยท runs in your browser ยท your draft is saved locally
๐ My notessaved in this browser