Binary search is the fastest way to find a value in a sorted array. Instead of scanning element by element, you jump straight to the middle, ask "is my target bigger or smaller than this?", and throw away the half that cannot contain it. Repeat, and the search space collapses by half every single step.
Step through the animation on the right. Watch the lo and hi pointers close in on the target. Each step the dimmed cells are the half we just ruled out — they never get looked at again.
The idea
Keep a window [lo, hi] that must contain the target. Look at the middle of the window. If the middle is the target, you are done. If the target is bigger, it must be in the right half, so move lo past the middle. If it is smaller, it must be in the left half, so move hi before the middle. Each comparison halves the window.
Because every round throws away half of what is left, an array of one million items takes only about 20 comparisons. That halving is the whole magic.
Walk through it
Press Play on the right, or step with Next / Back. We are searching for 16 in [2, 5, 8, 12, 16, 23, 38, 56]. Notice three things:
- The mid cell turns blue when we compare it to the target.
- The half we rule out turns gray (dimmed) and is never touched again.
- When
a[mid]finally equals the target, that cell turns green — found.
The search window shrinks from 8 cells, to 4, to 1. Three comparisons and we are done.
The code, line by line
def binary_search(a, target):
lo, hi = 0, len(a) - 1
while lo <= hi:
mid = (lo + hi) // 2
if a[mid] == target:
return mid
elif a[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1loandhiare the inclusive ends of the current search window. They start as the whole array.- The loop runs while the window is non-empty (
lo <= hi). mid = (lo + hi) // 2is integer division, so it floors to a valid index.- If
a[mid]is too small, the answer is to the right, so we setlo = mid + 1. If it is too big, the answer is to the left, so we sethi = mid - 1. We always move pastmid, which is what guarantees the loop ends. - If the window empties out, the value is not present and we return
-1.
Complexity
| Case | Time | Notes |
|---|---|---|
| Best | O(1) (fast) | target is the first middle we check |
| Average | O(log n) (fast) | |
| Worst | O(log n) (fast) | target absent, or at an edge |
O(1) (fast)Why O(log n)? Each comparison cuts the search space in half. You can only halve n about log₂(n) times before you reach a single element — that is roughly 20 steps for a million items, 30 for a billion. The space is O(1) because we only track three indices, no matter how big the array is.
When to use / pitfalls
Binary search is the go-to answer whenever the input is sorted and you are asked to find something
fast. It also generalizes far beyond arrays: "binary search on the answer" turns many optimization
problems into a yes/no check you can halve. The classic bug is an off-by-one in the bounds — get
comfortable with lo = mid + 1 and hi = mid - 1 and the lo <= hi loop condition.
Binary search only works on sorted data. On an unsorted array the "throw away half" logic is
meaningless and you will get wrong answers. If the data is not already sorted, sorting it first costs
O(n log n) — so for a single lookup a plain linear scan (O(n)) can actually be cheaper.
Practice
Searching for 16 in [2, 5, 8, 12, 16, 23, 38, 56], the first mid is index 3 (value 12). Which half do we keep next?
1. What must be true about the array for binary search to work?
2. If a[mid] is smaller than the target, what do we do?
3. Why is binary search O(log n)?
4. Roughly how many comparisons does binary search need for 1,000,000 sorted items?