← SciSim / Computer Science

🔍 Binary Search

Divide-and-Conquer | Sorted Arrays | O(log n)

HSC / Early Undergraduate

§1 — Interactive Simulation

Speed:
0
lo
-
mid
-
hi
0
Comparisons
BINARY-SEARCH(A, target):
  lo ← 0, hi ← n-1
  while lo ≤ hi:
    mid ← lo + (hi-lo)/2 // avoid overflow
    if A[mid] == target: return mid // found
    elif A[mid] < target: lo ← mid+1 // go right
    else: hi ← mid-1 // go left
  return -1 // not found

§2 — The Idea, Step by Step

Imagine looking up "zebra" in a paper dictionary. You don't start at "aardvark" and read every word — you flip near the back, see you've gone a little too far, and jump into the half that's left. Then you split that half, and again, until you land on the word. Binary search is exactly this trick applied to a sorted list: look in the middle, throw away the half that can't contain your target, and repeat.

The list has to be sorted first — that is the one rule. We keep three markers: lo (the low end of what's still possible), hi (the high end), and mid (the middle between them). Each step we compare the target with the value sitting at mid. If they match, we're done. If the target is larger, it can only be in the right half, so lo jumps to mid+1. If it's smaller, it's in the left half, so hi drops to mid−1. Every single comparison throws away half of what's left. Starting from n items, one step leaves n/2, two steps leave n/4, and so on. A sorted list of 1,000 names shrinks 500, 250, 125, 63, 32, 16, 8, 4, 2, 1 — just 10 comparisons to find anyone, instead of up to 1,000 if you checked them one by one.

How many halvings does it take to shrink n all the way down to 1? That's the question $n/2^k \lt 1$, which rearranges to $k \gt \log_2 n$. So binary search runs in $O(\log n)$ time, captured by the recurrence $T(n) = T(n/2) + 1$: doubling the amount of data adds only one extra comparison. A billion sorted items need about 30 steps. In the simulation above, the Array Size slider sets n, the Target box (or a preset) sets what you're hunting for, and the lo / mid / hi cards show the search window collapsing in real time.

Try this 1. Set Array Size to 32 and choose the Last element preset — watch it finish in about 5 comparisons instead of 32.
Try this 2. Pick the Not Found preset and step to the end: lo and hi cross over (lo > hi), which is exactly how the algorithm decides the value isn't there.
Try this 3. Search the Middle element (found on the very first comparison — best case O(1)), then search the first element. Same array, very different effort: that's the gap between best case and the full ⌊log₂n⌋ steps.

§3 — Complexity Analysis & Derivation

SymbolMeaning
nArray size
T(n)Max comparisons for array of size n
⌊log₂n⌋Floor of log base 2 of n
Step 1 — Recurrence. Each iteration halves the search space. If not found at mid: either lo = mid+1 (right half, size ⌊(n−1)/2⌋) or hi = mid−1 (left half, size ⌊n/2⌋). Worst case: T(n) = T(⌊n/2⌋) + 1.
Step 2 — Solve Recurrence. T(n) = T(n/2) + 1. By Master Theorem: a=1, b=2, f(n)=1=n⁰. log_b(a)=log₂1=0. f(n)=Θ(n⁰) → Case 2 → T(n)=Θ(log n).
Step 3 — Direct count. After k iterations, search space ≤ n/2^k. Terminates when n/2^k < 1, i.e., k > log₂n. So at most ⌊log₂n⌋+1 iterations. For n=1000: at most 10 comparisons. For n=10⁶: at most 20 comparisons.
Step 4 — Best case. Target is at mid on first try: O(1). Average case: O(log n) (target equally likely at any position).
Step 5 — Space. Iterative: O(1). Recursive: O(log n) call stack.
Step 6 — Prerequisite. Array MUST be sorted. Sorting costs O(n log n). Binary Search makes sense when: (a) searching multiple times (amortize sorting cost), or (b) array is already sorted.

§4 — Frequently Asked Questions

Q1. Why use mid = lo + (hi-lo)/2 instead of (lo+hi)/2?concept
If lo and hi are both large (e.g., near INT_MAX), then lo+hi can overflow a 32-bit integer. lo + (hi-lo)/2 computes the same value without overflow since hi-lo ≤ n and lo ≤ INT_MAX. This famous bug was found in Java's Arrays.binarySearch in 2006 after being published in "Programming Pearls" in 1986!
Q2. How does Binary Search apply to non-array problems?applied
"Binary search on the answer": if you can check whether a value x is feasible in O(f(n)), and feasibility is monotone (if x is feasible, x−1 is also feasible), then binary search finds the minimum/maximum feasible value in O(f(n) log(range)). Examples: finding minimum time to complete tasks with k workers, minimum capacity of a rope-cutting problem, square root approximation.
Q3. What is lower_bound vs upper_bound?concept
lower_bound(target): first index i where A[i] ≥ target. upper_bound(target): first index i where A[i] > target. Number of occurrences of target = upper_bound − lower_bound. These are standard in C++ STL and handle duplicates correctly. Pure binary search (exact match) doesn't distinguish these cases.
Q4. Can Binary Search work on a rotated sorted array?nonobv
Yes. LeetCode "Search in Rotated Sorted Array": check if left half [lo..mid] is sorted (A[lo] ≤ A[mid]). If target is in this sorted half, recurse there; otherwise recurse on right half. The rotation point can be found with binary search in O(log n) as well.
Q5. Why is Binary Search faster than Linear Search by such a large factor for large n?sim
For n=10⁶, linear search may take 500,000 comparisons on average, while binary search takes at most 20. Use the simulation: set n=32, try searching for the last element. Binary Search finds it in 5 steps; linear would take 32. The logarithm grows so slowly that even for n=10¹⁸ (all integers ever computed), binary search needs only 60 comparisons.
Q6. What is Fractional Cascading?deep
When binary searching the same target across k sorted arrays, naively costs O(k log n). Fractional Cascading: preprocess the arrays by merging elements from later arrays into earlier ones with pointers. After the first O(log n) search, subsequent searches in related arrays cost O(1) each — total O(log n + k). Used in computational geometry (point location in planar subdivision) and database index structures.

§5 — Common Misconceptions

Conceptual Misconceptions

❌ "Binary Search works on unsorted arrays."

✅ Binary Search requires sorted data. On unsorted data, eliminating a half-array is incorrect — the target could be anywhere.

❌ "Binary Search always checks exactly log₂n elements."

✅ log₂n is the upper bound. If the target is at the first mid position checked, it terminates in 1 step (best case O(1)).

❌ "Binary Search is always better than linear search."

✅ For very small arrays (n ≤ 8), linear search is faster due to branch prediction and no overhead. For nearly-sorted small arrays, linear scan is competitive.

Implementation Misconceptions

❌ Using mid = (lo+hi)/2 (potential integer overflow).

✅ Use mid = lo + (hi-lo)/2 or (lo+hi) >>> 1 (unsigned right shift in Java).

❌ Loop condition while (lo < hi) instead of while (lo <= hi) for exact match search.

✅ lo < hi exits when lo==hi without checking that last element. Use lo <= hi for exact match. For lower_bound/upper_bound, lo < hi is correct with different return logic.

❌ Infinite loop: forgetting to update lo or hi after mid doesn't match.

✅ Always: lo = mid+1 (right move) or hi = mid-1 (left move). Setting lo = mid or hi = mid without adjusting can loop forever when lo == hi == mid.