Koko Eating Bananas
Table of Contents + −
This one teaches a trick that feels like magic the first time you see it. You are not searching inside an array here. You are searching for the answer itself. The interviewer wants to see if you can spot when binary search applies even though there is no sorted array in sight. That idea is called binary search on the answer, and it unlocks a whole class of problems.
🎯 The Problem
Koko eats bananas from piles within a time limit. The rules:
- There are some piles. Each pile has a number of bananas.
- A guard is away for
hhours. - Koko picks one eating speed
k. That is how many bananas she eats per hour. - Each hour she eats from one pile only, up to
kbananas. - If a pile has fewer than
kbananas left, she eats it all and waits out the rest of that hour. - She does not start another pile in the same hour.
- Find the smallest speed
kthat finishes every pile withinhhours.
Let us say the piles are [3, 6, 7, 11] and h is 8. The smallest speed that works is 4. At speed 4 the pile of 3 takes 1 hour. The pile of 6 takes 2 hours. The pile of 7 takes 2 hours. The pile of 11 takes 3 hours. That adds up to 8 hours, which fits.
Input: piles = [3, 6, 7, 11], h = 8Output: 4
Explanation: at speed 4 the hours are 1 + 2 + 2 + 3 = 8, which is within h = 8Here is the key thing to notice. A bigger speed never needs more hours. So the speeds split into “too slow” and “fast enough”, and we want the first speed that is fast enough.
Here is the answer space. Speeds below 4 need too many hours. Speed 4 and above all finish in time. We want the boundary.
🐢 Approach 1: Try Every Speed (Brute Force)
Test each speed from slow to fast until one finishes in time.
The idea:
- Start at speed
1. - Add up the hours that speed needs across all piles.
- If it fits within
h, that speed is the answer. - If not, try speed
2, then3, and so on.
How it works:
- This is a linear scan over the possible speeds.
- A linear scan tries each value in order until one works.
- It always lands on the right speed.
Why it is weak:
- The top speed you ever need is the biggest pile.
- If the biggest pile has a million bananas, you might try a million speeds.
- For each speed you re-add all the piles.
- Time is O(n times max pile), where
nis the number of piles. Too slow.
Here is the try-every-speed code:
import math
def min_eating_speed(piles, h): for speed in range(1, max(piles) + 1): hours = sum(math.ceil(pile / speed) for pile in piles) if hours <= h: return speed⚡ Approach 2: Binary Search on the Answer (Best)
The speeds split into too-slow and fast-enough, so binary search the speed itself.
The idea:
- Speeds run from
1up to the biggest pile, already in order. - Every speed below the answer is too slow.
- Every speed from the answer up is fast enough.
- That clean split is what binary search needs.
The feasibility check:
- A feasibility check just tests whether one guessed speed works.
- For a pile of size
pat speedmid, the hours arepdivided bymid, rounded up. - Round up because a leftover bit of a pile still costs a full hour.
- Add the hours for all piles. Compare with
h.
How it works:
- Keep
loat1, the slowest speed. - Keep
hiat the biggest pile, since no faster speed is ever needed. - Pick the middle speed
mid. - If
midfits withinh, a slower speed might still work, so movehitomid. - If
middoes not fit, it is too slow, so movelotomid + 1. - Stop when
lomeetshi. That value is the smallest speed that works.
Why it is fast:
- Each step throws away half the speeds.
- About log of the biggest pile rounds, each checking all piles.
- Time is O(n times log of max pile).
Here is a dry run on [3, 6, 7, 11] with h = 8. Watch lo, mid and hi close in on speed 4.
Steps to Solve
- Set
loto1andhito the largest pile. - While
lois less thanhi, keep narrowing the window. - Find the middle speed
midaslo + (hi - lo) / 2. - Add up the hours at speed
mid. For each pile, add its size divided bymid, rounded up. - If the total hours are within
h, speedmidfits, so movehitomid. - If the total hours go over
h, speedmidis too slow, so movelotomid + 1. - When
loequalshi, that is the smallest speed. Return it.
This Python version uses a rounded-up division for the hours and binary searches the speed.
import math
def hours_needed(piles, speed): return sum(math.ceil(p / speed) for p in piles) # round each pile up
def min_eating_speed(piles, h): lo, hi = 1, max(piles) while lo < hi: mid = lo + (hi - lo) // 2 # middle speed if hours_needed(piles, mid) <= h: hi = mid # fits, try a slower speed else: lo = mid + 1 # too slow, go faster return lo # smallest speed that fits
piles = [3, 6, 7, 11]h = 8print(min_eating_speed(piles, h))The output of the above code will be:
4Let us walk through the Python version line by line, so the “search on the answer” idea sticks.
The function hours_needed(piles, speed) is the feasibility check. The line return sum(math.ceil(p / speed) for p in piles) adds up the hours. For each pile p, math.ceil(p / speed) is the pile size divided by the speed, rounded up. We round up because any leftover bananas still cost a full hour.
The line lo, hi = 1, max(piles) sets the speed range. The slowest useful speed is 1. The fastest you ever need is the biggest pile, because at that speed every pile takes just one hour.
The line while lo < hi: shrinks the speed window until the two edges meet on one speed.
The line mid = lo + (hi - lo) // 2 picks the middle speed to test. Same overflow-safe middle as plain binary search.
The line if hours_needed(piles, mid) <= h: runs the feasibility check at speed mid. If the hours fit within h, then mid works. But a slower speed might also work, so we are not done. We set hi = mid and keep mid in the window.
The else branch means speed mid is too slow. The hours went over h. So we set lo = mid + 1 to force a faster speed.
When the loop ends, lo and hi point at the same speed. The line return lo hands back the smallest speed that finishes in time.
⏱️ Time and Space Complexity
The brute force tries every speed up to the biggest pile, and checks all piles each time. That is O(n times max pile). Binary search on the answer only tries about log of the biggest pile speeds, and checks all piles each time. So it is O(n times log of max pile). Both use only a few variables, so the space is O(1).
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Try every speed (linear scan) | O(n times max pile) | O(1) |
| Binary search on the answer | O(n times log of max pile) | O(1) |
Tip
The signal for “binary search on the answer” is this. Bigger guesses always stay valid once they start working. When you see that clean too-slow then fast-enough split, search the answer range, not an array.
🧩 Key Takeaways
- ✅ You are searching for the answer speed, not searching inside an array.
- ✅ The speeds split into too-slow and fast-enough, which is perfect for binary search.
- ✅ Keep
loat1andhiat the biggest pile, since no faster speed is ever needed. - ✅ The feasibility check adds up hours, rounding each pile up, and compares with
h. - ✅ This runs in O(n times log of max pile), far faster than trying every speed.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What are we actually binary searching over in this problem?
Why: There is no sorted array to search. We binary search the answer range, the possible speeds from 1 to the biggest pile.
- 2
Why set hi to the biggest pile?
Why: At a speed equal to the biggest pile, each pile finishes in one hour, so a faster speed never helps.
- 3
When speed mid finishes within h hours, what do you do?
Why: Speed mid works, but a slower speed might also work, so we keep mid and search lower by setting hi to mid.
- 4
Why do we round the hours up for each pile?
Why: Koko cannot share an hour across two piles, so a partly eaten pile still uses a whole hour.