Binary search on the answer
Guess an answer, check feasibility greedily, tighten the range.
feasible(x) is monotone, so the smallest feasible x is a boundary.
01The intuition
Start with what you would do by hand
Koko has four piles of bananas: 3, 6, 7, 11. She eats at a fixed speed of k bananas per hour, finishing at most one pile per hour (if a pile has fewer than k she still spends the hour). The guards return in 8 hours. What is the slowest speed that still finishes every pile in time?
By hand: at speed 1 the piles take 3 + 6 + 7 + 11 = 27 hours, too slow. Speed 2: 2 + 3 + 4 + 6 = 15 hours. Speed 3: 1 + 2 + 3 + 4 = 10. Speed 4: 1 + 2 + 2 + 3 = 8. That fits, and 3 did not, so the answer is 4. You tried every speed from 1 up, and each try walked all the piles. If the biggest pile held a billion bananas, the answer could be around a billion, and you would do a billion tries.
Notice the pattern of the tries: too slow, too slow, too slow, fits, fits, fits, … Once a speed works, every faster speed works. That is a yes/no condition that is false and then true, and we already know how to find such a boundary quickly.
Build the structure, one piece at a time
Piece 1: a checker. Write feasible(k): hours needed at speed k is the sum over piles of ⌈pile / k⌉; return whether that is ≤ 8. This is a straight walk over the piles.
Piece 2: a range of possible answers. The slowest sensible speed is 1 and the fastest useful speed is the biggest pile, 11, since anything faster changes nothing. So lo = 1, hi = 11.
Piece 3: binary search the boundary. Test the middle speed. If it is feasible, the answer is mid or slower: hi = mid. If not, the answer is faster: lo = mid + 1. Stop when lo = hi.
| lo | hi | mid | hours at mid | feasible? | Move |
|---|---|---|---|---|---|
| 1 | 11 | 6 | 1+1+2+2 = 6 | yes | hi = 6 |
| 1 | 6 | 3 | 1+2+3+4 = 10 | no | lo = 4 |
| 4 | 6 | 5 | 1+2+2+3 = 8 | yes | hi = 5 |
| 4 | 5 | 4 | 1+2+2+3 = 8 | yes | hi = 4 |
| 4 | 4 | answer 4 | |||
Four checks instead of four tries here, but for a billion-banana pile it is about thirty checks instead of a billion.
Why it is correct
The whole method rests on one fact: feasible(k) is monotone. If speed k finishes in time, speed k + 1 does too, because each pile takes the same or fewer hours.
⌈p / (k + 1)⌉ ≤ ⌈p / k⌉ for every pile p, so hours(k + 1) ≤ hours(k)
Given monotonicity, the invariant is the boundary-search invariant: feasible(lo − 1) is false (or lo is the minimum speed) and feasible(hi) is true. Moving lo past an infeasible mid and hi onto a feasible mid keeps both facts, the range shrinks every step, and lo = hi is the smallest feasible speed.
The checker must be exact and greedy-simple. For "minimise the largest sum when splitting an array into m parts", the checker is: walk the array, start a new part whenever adding the next element would exceed the guessed limit, and count the parts. That greedy count is the fewest parts possible for that limit, so the check is exact.
Why it is fast
The number of checks is about log2 of the size of the answer range, and each check walks the n piles once.
time = O(n × log2(hi − lo)) = O(n log(range))
For 1000 piles and a range of a billion that is 1000 × 30 = 30 000 steps, instead of 1000 × a billion.
How to recognise it in an interview
The cues are "minimum speed / capacity / time such that", "minimise the maximum", "maximise the minimum", "smallest x that makes it possible". Whenever checking a given answer is easy but computing the answer directly is not, ask whether the check is monotone. Say: "I can verify a candidate in O(n) and feasibility is monotone, so I binary search over the answer."
Koko Eating Bananas is the example above. Capacity To Ship Packages Within D Days guesses a ship capacity and counts days greedily. Split Array Largest Sum guesses the largest allowed sum and counts parts. Swim in Rising Water guesses a water level and checks reachability with a flood fill, which shows the checker can be a graph search.
02The idea in one line
Guess an answer, check feasibility greedily, tighten the range.
Reach for it when: Split array largest sum, Koko bananas, ship capacity, minimize max / maximize min.
Cost: O(n log range) time.
03The walk-through, step by step
The video works one example: Koko Eating Bananas piles [3,6,7,11], h=8. These are its captions in order, so you can follow without the video or check a step you missed.
- Guess a speed k, check feasibility, then halve the k range
- k = 6: hours = 1 + 1 + 2 + 2 = 6 ≤ 8
- Feasible: try slower, hi = 6
- k = 3: hours = 1 + 2 + 3 + 4 = 10 > 8
- Too slow: need faster, lo = 4
- k = 5: hours = 1 + 2 + 2 + 3 = 8 ≤ 8
- Feasible: try slower, hi = 5
- k = 4: hours = 1 + 2 + 2 + 3 = 8 ≤ 8
- Feasible: try slower, hi = 4
- lo == hi: the minimum feasible speed is 4
- feasible(k) is monotone, so the answer is a boundary: O(n log range)
04Asked at Google
13 Google-tagged LeetCode problems use this pattern. Frequency is LeetCode's Google-tag score (higher means reported more often); "last 6 months" marks problems reported in the most recent window of the export.
| Problem | Difficulty | Frequency | Reported |
|---|---|---|---|
| Koko Eating Bananas | Medium | 59 | last 6 months |
| Split Array Largest Sum | Hard | 58 | last 6 months |
| Capacity To Ship Packages Within D Days | Medium | 44 | last 6 months |
| Swim in Rising Water | Hard | 41 | last 6 months |
| Minimum Number of Days to Make m Bouquets | Medium | 40 | last 6 months |
| Path With Minimum Effort | Medium | 38 | last 6 months |
| Maximum Candies Allocated to K Children | Medium | 32 | last 6 months |
| Find the Smallest Divisor Given a Threshold | Medium | 29 | |
| Kth Smallest Element in a Sorted Matrix | Medium | 26 | last 6 months |
| Magnetic Force Between Two Balls | Medium | 17 | last 6 months |
| Minimum Speed to Arrive on Time | Medium | 17 | |
| Minimum Time to Complete Trips | Medium | 13 | last 6 months |
| Minimized Maximum of Products Distributed to Any Store | Medium | 11 | last 6 months |
05Related patterns
Other patterns in Binary Search: Classic binary search, First true (predicate boundary), Rotated / mountain search, Partition two sorted arrays.