Binary search: the halving recurrence
Each step throws away half the remaining range, which gives the recurrence T(n) = T(n/2) + O(1) and a bound of O(log n).
Binary search only works on a sorted array, and the whole point of it is that every comparison halves the part I still have to look at.
The invariant#
I keep a range that is known to contain the answer, if the answer exists at all:
let lo = 0;
let hi = arr.length - 1;
while (lo <= hi) {
const mid = lo + Math.floor((hi - lo) / 2);
if (arr[mid] === target) return mid;
if (arr[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;The invariant is that the target, if present, sits inside . Every branch either returns or shrinks that range, so the loop has to terminate.
Using lo + (hi - lo) / 2 instead of (lo + hi) / 2 avoids overflowing an integer when the indices are large.
The recurrence#
Let be the work on a range of size . Each step does a constant amount of work and then recurses on at most half the range:
I unfold it times:
The recursion bottoms out when , i.e. when
Base of the logarithm is irrelevant#
Different sources write , or
And big-O throws constants away. So is the honest statement.
Why the constant matters in practice#
The bound is good enough that the only thing left to worry about is getting the bounds right — lo <= hi versus lo < hi, mid + 1 versus mid. Those aren't sloppy details; a wrong one turns a search into an infinite loop or a missed element.