Binary Search

Efficiently find an element in a sorted array by halving the search space at each step.

O(log n)Search
Phase 1f(x)

The Mathematics

Formal definition, invariant, and complexity analysis

Problem Definition

Given a sorted array A[0..n1]A[0..n-1] and a target value xx, find the index ii such that A[i]=xA[i] = x, or determine that xx is not in the array.

A[0]A[1]A[n1]A[0] \leq A[1] \leq \cdots \leq A[n-1]

Loop Invariant

At every iteration, if xx exists in AA, then it must lie within the current search bounds:

A[lo]xA[hi]A[\text{lo}] \leq x \leq A[\text{hi}]

The search space halves each step. We compute the midpoint and compare:

mid=lo+hi2\text{mid} = \left\lfloor \frac{\text{lo} + \text{hi}}{2} \right\rfloor

\bullet If A[mid]=xA[\text{mid}] = x, we found the target.

\bullet If A[mid]<xA[\text{mid}] < x, then lomid+1\text{lo} \leftarrow \text{mid} + 1.

\bullet If A[mid]>xA[\text{mid}] > x, then himid1\text{hi} \leftarrow \text{mid} - 1.

Complexity Analysis

Each step does O(1)O(1) work and halves the search space. This gives the recurrence:

T(n)=T(n/2)+O(1)T(n) = T(n/2) + O(1)

Solving (or by the Master Theorem, case 2 with a=1,b=2,f(n)=O(1)a=1, b=2, f(n)=O(1)):

T(n)=O(logn)T(n) = O(\log n)

Comparison with Linear Search

Linear search examines each element: T(n)=O(n)T(n) = O(n). For n=1,000,000n = 1{,}000{,}000, binary search needs at most log2106=20\lceil \log_2 10^6 \rceil = 20 comparisons vs. up to 10610^6 for linear search.

Phase 2

See It Work

Searching for 23 in a sorted array

20lo5182123164mid235386567728919hi
A[lo]xA[hi]A[lo] \leq x \leq A[hi]
Step 1 of 4

Initialize: search for 23 in array of 10 elements. Set lo=0, hi=9.

Phase 3</>

The Code

Bridge from mathematical formulation to Python implementation

Mathematical Formulation

lo=0,  hi=n1\text{lo} = 0,\; \text{hi} = n-1

Initialize the search bounds to cover the entire array

A[lo]xA[hi]A[\text{lo}] \leq x \leq A[\text{hi}]

Loop invariant: target must be within current bounds

mid=(lo+hi)/2\text{mid} = \lfloor(\text{lo} + \text{hi})/2\rfloor

Compute the midpoint using integer division

A[mid]=xreturnA[\text{mid}] = x \Rightarrow \text{return}

If midpoint value equals target, we found it

A[mid]<xlo=mid+1A[\text{mid}] < x \Rightarrow \text{lo} = \text{mid} + 1

Target is larger, eliminate the left half

A[mid]>xhi=mid1A[\text{mid}] > x \Rightarrow \text{hi} = \text{mid} - 1

Target is smaller, eliminate the right half

Python Implementation

def binary_search(A, x):
    lo, hi = 0, len(A) - 1        # Initialize search bounds
    while lo <= hi:                # Invariant: x in A[lo..hi]
        mid = (lo + hi) // 2      # Compute midpoint
        if A[mid] == x:
            return mid             # Found: return index
        elif A[mid] < x:
            lo = mid + 1           # Eliminate left half
        else:
            hi = mid - 1           # Eliminate right half
    return -1                      # Not found