Algorithms

Binary Search

Find one item in a million in 20 lookups

Binary search is a divide-and-conquer search algorithm that finds a target value in a sorted array by repeatedly halving the search range. It runs in O(log n) time, meaning a one-million-element array takes at most 20 comparisons to search.

  • Time (worst, average)O(log n)
  • Time (best)O(1)
  • Space (iterative)O(1)
  • Requires sorted inputYes
  • StableN/A (search, not sort)

Interactive visualization

Press play, or step through manually. The visualization is yours to drive — try it before reading on.

Open visualization fullscreen ↗

Watch the 60-second explainer

A condensed visual walkthrough — narrated, captioned, under a minute.

How binary search works

Imagine looking up a word in a printed dictionary. You don't start from page 1 — you flip to roughly the middle. If the word you want comes earlier alphabetically, you ignore the back half and repeat with the front half. Binary search does the same thing, on a sorted array, in code.

The algorithm tracks two indices, low and high, marking the boundaries of the range that could still contain the target. On each iteration:

  1. Compute the midpoint: mid = floor((low + high) / 2).
  2. Compare arr[mid] to the target.
  3. If equal, return mid — found.
  4. If arr[mid] is smaller, the target must be to the right; set low = mid + 1.
  5. If arr[mid] is larger, set high = mid - 1.
  6. Repeat until found, or until low > high (target absent).

The halving step is the magic. With a million-item array, the range halves to 500,000 → 250,000 → 125,000 → ... → 1 in just 20 comparisons. Linear search would average 500,000.

When to use binary search

  • Your data is sorted, or you can afford to sort it once and search many times.
  • You need an exact match, or the leftmost or rightmost match.
  • Random-access reads are cheap (an array, not a linked list).
  • You want predictable worst-case performance — binary search has no pathological inputs.

If your data changes constantly, the cost of keeping it sorted may outweigh the search savings. A hash table gives O(1) average lookups for unordered point queries. A balanced binary search tree gives O(log n) lookups and cheap ordered traversal.

Linear searchBinary search
Time complexity (worst)O(n)O(log n)
Requires sorted inputNoYes
1,000,000 items (worst case)1,000,000 comparisons20 comparisons
Best forTiny or unsorted arraysSorted arrays, repeated queries

Linear search beats binary search only when the array is small (under roughly 30 elements, where the log-factor advantage is dwarfed by control overhead) or when the cost of sorting once exceeds the savings across all future queries.

Pseudo-code

function binarySearch(arr, target):
    low = 0
    high = arr.length - 1
    while low <= high:
        mid = floor((low + high) / 2)
        if arr[mid] == target:
            return mid
        if arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1   # not found

JavaScript implementation

function binarySearch(arr, target) {
  let low = 0;
  let high = arr.length - 1;
  while (low <= high) {
    const mid = (low + high) >>> 1;  // unsigned shift = safe integer divide
    if (arr[mid] === target) return mid;
    if (arr[mid] < target) low = mid + 1;
    else high = mid - 1;
  }
  return -1;
}

The (low + high) >>> 1 trick avoids integer overflow on huge arrays — the same bug that lived in Java's standard library for nearly a decade before being fixed in 2006.

Python implementation

def binary_search(arr, target):
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        if arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

Python's standard library also offers bisect.bisect_left and bisect.bisect_right — implementations of the leftmost/rightmost variants that you should use in production rather than rolling your own.

Common bugs and edge cases

  • Off-by-one in the loop condition. Use low <= high, not low < high — the latter misses the case where low and high converge on the target index.
  • Integer overflow on (low + high). In languages with fixed-width integers, prefer low + (high - low) / 2 or an unsigned shift.
  • Forgetting to update boundaries. Always set low = mid + 1 or high = mid - 1, not mid, or the loop spins forever.
  • Searching unsorted data. The algorithm returns garbage silently — there's no runtime check.

Frequently asked questions

Why is binary search O(log n)?

Each comparison eliminates half of the remaining search range. Halving an n-element range repeatedly takes log₂(n) steps. For n = 1,000,000, that's about 20.

Does binary search work on unsorted arrays?

No. The whole algorithm depends on knowing that items left of the midpoint are smaller and items right are larger. Without sorted order, halving the range tells you nothing useful.

Is binary search faster than a hash table?

For a single lookup in unsorted data, no — hash tables are O(1) average. But binary search wins when you need ordered queries (next-greater, range, leftmost match) or when the data is already sorted on disk.

What if the array contains duplicates?

Plain binary search returns any one matching index, undefined which. To find the leftmost or rightmost occurrence, modify the algorithm to keep searching one side after a match instead of returning immediately.

Can binary search work on a linked list?

Technically yes, but it's O(n log n) instead of O(log n) because each midpoint access requires traversal. Use a balanced BST or skip list for log-time search on linked structures.