Binary Search
A fast search that halves the range at every step in a sorted array.
Worth knowing first
#How does it work?
Binary search finds a target in a sorted array by halving the search range at every step. It's like looking up a word in a dictionary: open somewhere in the middle and check whether your word comes before or after.
The middle element of the range is compared with the target. If they are equal, we're done. If the middle is smaller, the target is in the right half; if larger, in the left half. The other half is discarded entirely.
Because the range halves every step, even an array of a million elements needs at most 20 comparisons. The only requirement is that the data is sorted.
#Live animation
The array is sorted. Target: 24. The search range is the whole array.
lo = 0; hi = n - 1while lo <= himid = (lo + hi) / 2if a[mid] == target: return midif a[mid] < target: lo = mid + 1else: hi = mid - 1return -1
#Step by step
- 01Take the whole array as the range: lo = 0, hi = n - 1.
- 02Find the middle: mid = (lo + hi) / 2.
- 03If a[mid] equals the target, return mid.
- 04If a[mid] is smaller than the target, discard the left half: lo = mid + 1. If larger, discard the right half: hi = mid - 1.
- 05Repeat until lo passes hi; an empty range means the value isn't there.
#Example code
The same algorithm in 8 languages. Every program is compiled, run and checked to print the same output.
def binary_search(a: list[int], target: int) -> int:
"""a must be sorted. Returns the index of target, or -1."""
lo, hi = 0, len(a) - 1
while lo <= hi:
mid = (lo + hi) // 2
if a[mid] == target:
return mid
if a[mid] < target:
lo = mid + 1 # target is in the right half
else:
hi = mid - 1 # target is in the left half
return -1
numbers = [1, 3, 5, 7, 9, 11]
print(binary_search(numbers, 7))
print(binary_search(numbers, 4))#Complexity
| Best | O(1) |
|---|---|
| Average | O(log n) |
| Worst | O(log n) |
| Extra memory | O(1) |
| Needs sorted data? | Yes |
An array of n elements shrinks to one element in at most log₂ n + 1 steps: 10 comparisons for 1,000 elements, 20 for 1,000,000. The loop version uses no extra memory; a recursive one uses O(log n) stack. Watch out: on very large arrays (lo + hi) can overflow when computing mid; lo + (hi - lo) / 2 is safer.
#When to use it?
Essential when sorted data is searched repeatedly: database indexes, dictionaries, sorted arrays (Java's Arrays.binarySearch, Python's bisect). The same idea, "binary search on the answer", solves many optimisation problems. If data changes often and can't be kept sorted, hash tables or balanced trees fit better.
#Practice
Log in to answer questions and save your progress.
Easy
What must be true of the data for binary search to work?
- 1It must be sorted
- 2It must have no duplicates
- 3It must be in a linked list
- 4It must have an even number of elements
What is binary search's worst-case complexity?
- 1O(log n)
- 2O(n)
- 3O(1)
- 4O(n log n)
If the middle element is smaller than the target, what happens?
- 1The left half is discarded and the right half is searched
- 2The right half is discarded and the left half is searched
- 3The search stops
- 4The array is re-sorted
Medium
Searching for 23 in [2, 5, 8, 12, 16, 23, 38], which element is checked first (mid)?
- 112
- 216
- 323
- 48
Hard
At most about how many comparisons does binary search make on a sorted array of 1,000,000 elements?
- 120
- 21,000
- 3500,000
- 41,000,000
#Up next
Lessons that build on this one: