Abstract

Binary Search is only applicable if the list is sorted. It uses the “Decrease and Conquer” strategy to eliminate half of the search space with every comparison.

  • Category: Searching / Decrease and Conquer
  • Input: Target value ; a sorted (increasing order) array
  • Output: The index of in the array, or an indication that it’s not present
  • Paradigm: Decrease and Conquer (each step shrinks the problem by a constant fraction, rather than splitting into multiple independent subproblems like Divide and Conquer does)
  • Typical use cases: searching sorted arrays, finding insertion points, as a building block for range queries and lower/upper-bound lookups

Core Logic (High-Level)

  1. Divide: Identify the mid-point of the current list. Divide the search space into two conceptual halves:
    • — the left half.
    • — the right half.
  2. Compare: Check the target element against the value at the mid-point:
    • Smaller: search the left half.
    • Greater: search the right half.
    • Equal: return the current position (target found).
  3. Recurse: continue splitting and searching until only one element remains.
  4. Terminate: if the search space is exhausted without a match, report that the item is not found.

Key Idea

Because the list is sorted, comparing against a single mid-point tells you which entire half can be safely thrown away — you never need to check it. That’s what makes this “decrease” rather than “divide”: only one half is ever explored, not both.


Pseudocode (Mid-Level Implementation)

Algorithm 6 Binary Search

Input: xx: integer to search for

Input: [a1,a2,,an][a_1, a_2, \dots, a_n]: array of increasing ordered integers to search in

Output: Index of where the interger is in the array (00 if input not in array)

procedure BinarySearch(x,[a1,a2,,an]x, [a_1, a_2, \dots, a_n])

lo=1lo = 1

hi=nhi = n

while lohilo \leq hi do

m=(lo+hi)2m = \lceil \frac{(lo + hi)}{2} \rceil

if x==amx == a_m then

return mm

if x<amx < a_m then

hi=m1hi = m-1

if x>amx > a_m then

lo=m+1lo = m+1

return 00

Variables & Data Structures

NameTypePurpose
lo, hiInteger indicesBound the current search space (inclusive, 1-indexed here); the loop narrows this range every iteration
mInteger indexThe current mid-point being compared against;
xValueThe target being searched for
aSorted arrayThe list being searched; requires random access by index

Index Convention

This pseudocode is 1-indexed ( starts at 1), while the conceptual “Divide” step above describes the halves as and using 0-indexing. Both describe the same idea — just double check which convention you’re using when implementing, since off-by-one errors here are the single most common bug in binary search.

Helper Functions / Operations Used

  • Random access a[i] — must be ; this is the one hard requirement on the data structure (see Drawbacks / Constraints).
  • Ceiling division ⌈(lo+hi)/2⌉ — picks the upper mid-point on ties; picking the floor instead also works, as long as the corresponding bound updates (hi = m-1 / lo = m+1) stay consistent with whichever rounding you chose, to guarantee the range always shrinks.

Low-Level Implementation

The version above is iterative, using extra space. A recursive version is often written to mirror the inductive proof below directly (call on the sub-array of size ), but that costs extra space for the recursion stack — see Time & Space Complexity Analysis.


Proof of Correctness

Claim: Binary Search correctly finds the target in a sorted list of size (proof by strong induction on ).

  • Base Case (): the mid-point is the only element. The algorithm checks it directly and correctly returns the index or reports “not found.”
  • Inductive Hypothesis: assume Binary Search is correct for all sorted lists of size .
  • Inductive Step: for a list of size , the algorithm compares against the mid-point.
    • If equal, it returns correctly.
    • If not equal, it recurses on a sub-list of size roughly — and since the list is sorted, the target (if present) is guaranteed to be entirely within whichever half was kept, never the discarded half.
    • Since , the Inductive Hypothesis applies, and the sub-search is guaranteed to be correct.

Termination: each iteration strictly shrinks the range — decreases every time, since either hi = m-1 < m ≤ hi or lo = m+1 > m ≥ lo. So after finitely many iterations, either the target is found, or and the loop exits, correctly reporting “not found” since every remaining candidate has been ruled out by the sorted-order comparisons along the way.


Time & Space Complexity Analysis

The efficiency of Binary Search comes from how quickly it shrinks the input. Each “split” reduces the remaining work by half.

Comparison Scaling

As the input size roughly doubles, the number of required comparisons only increases by 1:

n# of splits# of comparisons (k)
101
312
723
1534
3145

Deriving the Complexity

If the list size is , you need comparisons. Solving for in terms of :

\begin{align*} n &\leq 2^k - 1 \ n+1 &\leq 2^k \ \log_{2}(n+1) &\leq k \ k &= \boxed{\lceil\log_{2}(n+1)\rceil} \end{align*}

This confirms that Binary Search grows at a logarithmic rate, , making it incredibly efficient for large datasets.

General Case

ComplexityNotes
TimeEach comparison eliminates half the remaining search space
Space iterative / recursiveIterative version only needs lo, hi, m; recursive version accumulates one stack frame per halving

Implementation-Dependent Variations

Data Structure ChoiceImpact on TimeImpact on SpaceNotes
Array (contiguous, random access) totalThe standard case — this is what makes mid-point access possible
Linked List total — finding each mid-point takes since there’s no random accessEffectively defeats the purpose of binary search; see Drawbacks
Iterative vs. recursiveSame asymptotic time vs. Recursive is closer to the inductive proof’s structure but costs stack space

Best / Worst / Average Case

  • Best case: — the target happens to be exactly at the first mid-point checked.
  • Worst case: — target is not present, or is found only at the very last possible comparison.
  • Average case: — dominated by the same halving regardless of where the target sits, since even a “lucky” run only saves a constant number of comparisons off the bound.

Drawbacks / Constraints

  • Preconditions: the list must already be sorted. If it isn’t, Binary Search’s comparisons give no reliable information about which half to discard, and it will silently produce wrong results rather than erroring out.
  • Requires random access. Data structures without indexed access (e.g. a linked list) force just to locate each mid-point, which erases the entire benefit — use a structure like an array, or a balanced BST if the data also needs to change frequently.
  • Not suitable for: frequently-changing (dynamic) datasets, since keeping an array sorted after insertions/deletions costs per update to shift elements. A balanced BST (or skip list) supports both search and insert/delete, at the cost of not being a flat array.
  • Alternatives to consider: a hash table for average lookup when you don’t need sorted order or range queries at all; Computer Science Theory/Discrete Structures/Discrete Algorithms/Recursive Algorithms/Divide and Conquer/Merge Sort (or any sort) as the standard way to get a list sorted in the first place before searching it.

References / Links