Binary Search
Binary search speeds up a search by repeatedly halving the active interval. The classic use is looking up a value in a sorted array, but the same split idea powers lower/upper bounds, “binary search on the answer,” continuous root finding, and jump searches on trees.
This lesson follows CP-Algorithms — Binary Search.
Search in sorted arrays
Given a sorted array A₀ ≤ A₁ ≤ … ≤ Aₙ₋₁, is k present?
Linear scan is O(n) and ignores sortedness. Binary search keeps indices L < R with A_L ≤ k ≤ A_R (so if k appears at all, it lies in that range). Pick a midpoint M with L < M < R:
- if
A_L ≤ k ≤ A_M→ shrink to[L, M] - if
A_M ≤ k ≤ A_R→ shrink to[M, R]
When R = L + 1, compare against the two endpoints. In the worst case you always keep the larger half, so choose
M ≈ (L + R) / 2
to minimize max(M−L, R−M). Each step halves the gap; after h steps (R−L) / 2ʰ ≈ 1, so h ∈ O(log n).
For n ≈ 10⁶ ≈ 2²⁰ that is ~20 probes instead of a million.
Lower bound and upper bound
Often you want:
- lower bound of
k— first index withAᵢ ≥ k - upper bound of
k— first index withAᵢ > k
Together they form the half-open range of elements equal to k. Presence check: find the lower bound and test equality.
Implementation (half-open invariant)
Maintain L < R with A_L ≤ k < A_R. The active interval is [L, R).
Sentinels: L = -1, R = n, imagining A_{-1} = −∞ and A_n = +∞. Never read A_L or A_R while they are sentinels. Midpoint:
m = ⌊(L + R) / 2⌋
// a[0..n) sorted
int l = -1, r = n;
while (r - l > 1) {
int m = l + (r - l) / 2; // safer than (l+r)/2
if (k < a[m]) {
r = m; // a[l] ≤ k < a[m] ≤ a[r]
} else {
l = m; // a[l] ≤ a[m] ≤ k < a[r]
}
}
// l = last index with a[l] ≤ k (or -1)
// r = first index with a[r] > k (or n) → upper bound
(l + r) / 2 can overflow for large positive l and r (infamous JDK bug). Prefer l + (r - l) / 2, or C++20 std::midpoint(l, r).
🧪 Lower / upper bound on a sorted array
Interactive walkthrough
Step through the half-open search. Watch L / R / m and the invariant a[L] ≤ k < a[R].
Check: sorted-array search
Binary search on arrays
1 / 4Worst-case probes for binary search on n sorted elements is…
Pick an answer to see if you are right.
Search on an arbitrary predicate
Let f : {0,…,n−1} → {0,1} be monotone: f(0) ≤ f(1) ≤ … ≤ f(n−1).
Binary search finds the transition: largest l with f(l)=0 and f(l+1)=1 (if a flip exists). Same skeleton — replace k < a[m] with f(m):
int l = -1, r = n;
while (r - l > 1) {
int m = l + (r - l) / 2;
if (f(m)) {
r = m; // f(l)=0, f(m)=1
} else {
l = m; // f(m)=0, f(r)=1
}
}
Invariant: f(l)=0, f(r)=1 (with sentinels for all-0 / all-1 edge cases).
Useful when evaluating f is expensive — you only pay O(log n) calls.
🧪 Binary search on the answer (minimum capacity)
Binary search on the answer
When you can only check “is the answer ≥ λ?” with a monotone predicate, binary-search λ.
Example idea: maximize a floored average over long enough subarrays. Checking “average ≥ λ” rewrites as “some subarray of aᵢ − λ has length ≥ x+1 and nonnegative sum” (prefix sums). Binary-search λ over the value domain.
Continuous search
If f is continuous on [L, R] and (w.l.o.g.) f(L) ≤ f(R), intermediate-value theorem says every y ∈ [f(L), f(R)] has some x with f(x)=y. Halve [L, R] by comparing f(M) to y. Accuracy ±δ costs O(log((R−L)/δ)) — no monotonicity required.
Classic: roots of odd-degree polynomials (f → −∞ and +∞ at the ends).
Search with powers of 2
Instead of an interval, keep pointer i and exponent k. Test i + 2ᵏ; advance if the predicate is still 0, then decrease k. Common on trees (LCA, k-th ancestor) and Fenwick “find k-th nonzero.”
Check: predicates & variants
Binary search variants
1 / 4Predicate binary search requires f to be…
Pick an answer to see if you are right.
Practice drills
Sandbox versions of classic interview binary-search problems. Starters fail until the logic is right.
Sorted unique nums. Return the index of target, or where it would be inserted to keep order (lower bound).
Input: n target then n ints.
Contest / interview problems
Solve on LeetCode (same ideas as the drills).
Find First and Last Position of Element in Sorted Array
Judge: LeetCode 34
Given a sorted array (duplicates allowed), return [first, last] indices of target, or [-1, -1].
Lower bound + upper bound (or two binary searches). Exactly the half-open equal range.
Cheat sheet
| Situation | What to binary-search |
|---|---|
| Sorted array membership | Index interval with a[l] ≤ k < a[r] |
| First ≥ / first > | Lower / upper bound |
| Expensive monotone yes/no | Predicate f |
| Optimize a numeric answer | Feasibility check on λ |
Continuous f(x)=y | Halve real interval to ±δ |
| Trees / Fenwick | Powers of two from a pointer |
Previous: Basic Geometry · 0-1 BFS
Source: CP-Algorithms — Binary Search