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)
- Divide: Identify the mid-point of the current list. Divide the search space into two conceptual halves:
- — the left half.
- — the right half.
- 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).
- Recurse: continue splitting and searching until only one element remains.
- 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: : integer to search for
Input: : array of increasing ordered integers to search in
Output: Index of where the interger is in the array ( if input not in array)
procedure BinarySearch()
while do
if then
return
if then
if then
return
Variables & Data Structures
| Name | Type | Purpose |
|---|---|---|
lo, hi | Integer indices | Bound the current search space (inclusive, 1-indexed here); the loop narrows this range every iteration |
m | Integer index | The current mid-point being compared against; |
x | Value | The target being searched for |
a | Sorted array | The 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) |
|---|---|---|
| 1 | 0 | 1 |
| 3 | 1 | 2 |
| 7 | 2 | 3 |
| 15 | 3 | 4 |
| 31 | 4 | 5 |
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
| Complexity | Notes | |
|---|---|---|
| Time | Each comparison eliminates half the remaining search space | |
| Space | iterative / recursive | Iterative version only needs lo, hi, m; recursive version accumulates one stack frame per halving |
Implementation-Dependent Variations
| Data Structure Choice | Impact on Time | Impact on Space | Notes |
|---|---|---|---|
| Array (contiguous, random access) | total | The standard case — this is what makes mid-point access possible | |
| Linked List | total — finding each mid-point takes since there’s no random access | Effectively defeats the purpose of binary search; see Drawbacks | |
| Iterative vs. recursive | Same 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
- Linear Search vs Binary Search — visualizing why is asymptotically faster.
- Computer Science Theory/Discrete Structures/Discrete Algorithms/Recursive Algorithms/Divide and Conquer/Merge Sort — the most common way to ensure a list is sorted before searching.
- Asymptotic Notation — more on the notation used here.