Introduction

Searching asks whether a key xx occurs in a collection and, if so, where. It is one of the most basic operations, and the choice of method depends on what is known about the data:

  • Unsorted array. only linear search is possible, Θ(n)\Theta(n) in the worst case.
  • Sorted array with random access. binary search, Θ(lg⁡n)\Theta(\lg n) in the worst case, which is optimal for comparison-based search.
  • Dynamic sets. binary search trees search in O(h)O(h), which can be O(n)O(n) in the worst case; balanced search trees guarantee O(lg⁡n)O(\lg n) search with fast updates; hash tables give expected O(1)O(1) search but no order.

This note treats linear and binary search, the boundary variants of binary search, and the lower bound that makes binary search optimal.

Prerequisites

Binary search variants

The plain version returns some matching index. With duplicates, the first or last occurrence, or a count, may be needed instead.

First occurrence (lower bound)

Text
FIRST-OCCURRENCE(A, n, x)        // smallest i with A[i] >= x, or n+1
    lo <- 1
    hi <- n + 1
    while lo < hi
        mid <- floor((lo + hi) / 2)
        if A[mid] < x then lo <- mid + 1
        else hi <- mid
    return lo

The invariant is A[1..lo−1]<x≤A[hi..n]A[1..lo-1] < x \le A[hi..n] (treating A[n+1]A[n+1] as +∞+\infty). The loop always terminates because mid<himid < hi, so hi <- mid shrinks the range. Then xx is present if and only if lo≤nlo \le n and A[lo]=xA[lo] = x.

Replacing < by <= in the test gives the smallest ii with A[i]>xA[i] > x (the upper bound). The number of occurrences of xx is (upper bound) minus (lower bound), found with two binary searches in Θ(lg⁡n)\Theta(\lg n) time. For A=[2,4,4,4,7,9]A = [2, 4, 4, 4, 7, 9] and x=4x = 4: lower bound 2, upper bound 5, count 3.

Binary search on a monotone predicate

Binary search works on any predicate P(i)P(i) that is false for all ii below some threshold and true from it onwards: find the first true index in O(lg⁡n)O(\lg n) evaluations. Examples: the smallest kk with k2≥Nk^2 \ge N (integer square root), the first bad version in a sequence of builds, or the minimum capacity that lets a schedule finish in time.

Two standard problems reduce to this form.

  • Fixed point. Given sorted distinct integers A[1..n]A[1..n], find ii with A[i]=iA[i] = i. Distinct integers rise by at least 1 per step, so A[i]−iA[i] - i is non-decreasing and P(i)=(A[i]−i≥0)P(i) = (A[i] - i \ge 0) is monotone. Find the first true ii and test A[i]=iA[i] = i. For A=[−10,−5,0,4,7,9,20]A = [-10, -5, 0, 4, 7, 9, 20]: probes at 4 (A[4]−4=0A[4] - 4 = 0, go left), 2 (−7-7, go right), 3 (−3-3, go right), and the answer is i=4i = 4. With duplicates allowed the predicate is no longer monotone and no o(n)o(n) bound holds in general.
  • Maximum of a bitonic array (strictly increasing, then strictly decreasing). Use P(i)=(A[i]>A[i+1])P(i) = (A[i] > A[i+1]), which is false on the rising part and true from the peak on. For A=[1,3,8,12,9,5,2]A = [1, 3, 8, 12, 9, 5, 2]: probes at 4 (12>912 > 9, go left), 2 (3<83 < 8, go right), 3 (8<128 < 12, go right); the peak is at index 4. Equal neighbours break the method, so check that the question says "strictly".

Searching without a known length

To search a sorted sequence of unknown length, require an infinite sequence or a safe accessor returning an out-of-range sentinel treated as +∞+\infty. Probe index 1 first: if its value is at least xx, test that element (or report absence for the sentinel). Otherwise probe 2,4,8,…,2j2,4,8,\ldots,2^j until the value is at least xx or the sentinel is reached, then binary search the integer interval (2j−1,2j](2^{j-1},2^j] with the same safe accessor, never treating the sentinel as a real match. For a present key at position pp, time is O(1+lg⁡p)O(1+\lg p).

Related material. These methods are not named in the GATE syllabus. They are included because they are commonly studied together with binary search.

  • Ternary search splits into three parts with two comparisons per step: about 2log⁡3n≈1.26lg⁡n2 \log_3 n \approx 1.26 \lg n comparisons, more than binary search's lg⁡n\lg n. Binary search is preferred for sorted-array membership because ternary search uses more comparisons. Ternary search also has a common application in unimodal-function optimization.
  • Interpolation search guesses the position from the key's value. On keys drawn uniformly at random it averages O(lg⁡lg⁡n)O(\lg \lg n) probes, but its worst case is Θ(n)\Theta(n).

Important properties and observations

  • Binary search needs both sorted order and random access. On a sorted linked list it still costs Θ(n)\Theta(n) because reaching the middle takes linear time.
  • Sorting first and then binary searching costs Θ(nlg⁡n)+k⋅Θ(lg⁡n)\Theta(n \lg n) + k \cdot \Theta(\lg n) for kk searches; this beats kk linear searches only when kk is larger than about lg⁡n\lg n.
  • Worst-case probes for n=1,2,3,4,7,8,15,16n = 1, 2, 3, 4, 7, 8, 15, 16: 1, 2, 2, 3, 3, 4, 4, 5. The count increases exactly when nn reaches a power of 2.
  • The final value of lolo in an unsuccessful search is the insertion position; this is how sorted insertion and "rank" queries are answered.

GATE examination perspective

  • Traces. Follow lolo, hihi and midmid row by row as in the worked example; common errors are rounding midmid up instead of down, or mixing 0-based and 1-based indices.
  • Counting. "maximum number of comparisons" is ⌊lg⁡n⌋+1\lfloor \lg n \rfloor + 1 three-way probes; check whether two-way comparisons are counted instead.
  • Correctness of code variants. look for lo = mid without a matching while lo < hi and rounding rule (infinite loop), missing + 1 or - 1, and wrong loop conditions that skip the last element.
  • Disguised binary search. fixed point in sorted distinct integers, peak of a bitonic array, first true value of any monotone predicate: Θ(lg⁡n)\Theta(\lg n).
  • Complexity traps. binary search on a linked list is Θ(n)\Theta(n); counting occurrences in a sorted array is Θ(lg⁡n)\Theta(\lg n), not Θ(lg⁡n+k)\Theta(\lg n + k).

Summary

Sources

  1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 3rd ed., MIT Press, 2009: Exercise 2.3-5 (binary search and its Θ(lg n) worst case) and Section 8.1 (decision-tree lower bounds).

Next steps