
Week 3, Tuesday (Video)
January 20, 2026
I’m thinking of a number between 0 and 63.
You can ask yes/no questions.
What’s your guaranteed winning strategy?
Start with \(n = 64\) possibilities (0 to 63).
After each question: \(n \to n/2\)
Questions needed: \(\log_2(64) = 6\)
I’m thinking of a number between 0 and 63.
You can ask yes/no questions.
Given a sorted array and a target value, find the target’s index (or determine it’s not there).
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 2 | 5 | 8 | 12 | 16 | 23 | 38 | 56 | 72 | 91 |
Find target = 23.
Worst case: \(\Theta(n)\)
Worst case: \(\Theta(\log n)\)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 2 | 5 | 8 | 12 | 16 | 23 | 38 | 56 | 72 | 91 |
Find target = 23:
lo=0, hi=9, mid=4 → arr[4]=16 < 23 → lo=5lo=5, hi=9, mid=7 → arr[7]=56 > 23 → hi=6lo=5, hi=6, mid=5 → arr[5]=23 ✓ → return 5“I’ve assigned binary search in courses at Bell Labs and IBM… and I’ve studied their solutions… In the history of this exercise, only about ten percent of professional programmers have gotten the program right.”
— Jon Bentley, Programming Pearls (1986)
The first correct binary search was published in 1946.
The first bug-free implementation? 1962.
Invariant: If target is in arr, then target is in arr[lo:hi+1].
Equivalently: We haven’t excluded any index where target could be.
Before the first iteration: lo = 0, hi = len(arr) - 1
arr[lo:hi+1] = arr[0:len(arr)] = entire array
If target is in arr, it’s in arr[lo:hi+1]. ✓
mid is Always Valid
Since lo <= hi (loop condition) and mid = (lo + hi) // 2:
\[\texttt{lo} \leq \texttt{mid} \leq \texttt{hi}\]
This means arr[mid] is always a valid access inside the loop.
arr[mid] == target → we return mid.
Since lo <= mid <= hi, we know mid is in the search range.
We found the target and return its index. ✓
arr[mid] < target

Setting lo = mid + 1 excludes only indices where target can’t be. ✓
arr[mid] > target

Setting hi = mid - 1 excludes only indices where target can’t be. ✓
lo > hi)The loop exits when lo > hi.
At this exit point, arr[lo:hi+1] is empty.
The invariant says: if target is in arr, it’s in this empty range.
But nothing is in an empty range.
Therefore: target is not in arr. Return -1. ∎
Theorem: binary_search(arr, target) returns the index of target if present, else -1.
Proof: By induction on iteration count.
target ∈ arr, then target ∈ arr[lo:hi+1]When low + high > Integer.MAX_VALUE, this overflows.
Fix:
When you’re unsure about an edge case, ask:
“Does this maintain the invariant?”
lo = mid + 1: Does this exclude only impossible locations? Yes.hi = mid - 1: Does this exclude only impossible locations? Yes.lo <= hi: When does the search space become empty? When lo > hi.Each iteration:
How many halvings until range is empty?
\(\log_2(n)\)
Worst case: \(\Theta(\log n)\)
| \(n\) | Linear Search | Binary Search |
|---|---|---|
| 1,000 | 1,000 | 10 |
| 1,000,000 | 1,000,000 | 20 |
| 1,000,000,000 | 1,000,000,000 | 30 |
Binary search requires the array to be sorted.
What if it’s not sorted?
Use binary search when:
Don’t use when:
Binary search finds a target in \(\Theta(\log n)\) time (vs \(\Theta(n)\) for linear)
The invariant: “If target exists, it’s in arr[lo:hi+1]”
The proof: Init, Maintenance (3 cases), Termination
The bugs: Off-by-one errors, infinite loops, integer overflow
The precondition: Array must be sorted
Recursion: When the function calls itself.
Binary search: \(\Theta(\log n)\)
The invariant makes it correct.
The halving makes it fast.