30.Koko Eating Bananas
Problem
There are n piles of bananas; pile i has piles[i] bananas. The guards return in h hours. Koko picks an eating speed k (bananas per hour). Each hour she chooses one pile and eats k bananas from it; if the pile has fewer than k bananas she finishes it and does not move to another pile that hour.
Return the minimum integer k such that Koko can eat all bananas within h hours.
Examples
Input: piles = [3, 6, 7, 11], h = 8
Output: 4
Explanation: At k = 4 the piles take 1 + 2 + 2 + 3 = 8 hours. At k = 3 they take 1 + 2 + 3 + 4 = 10 > 8.Input: piles = [30, 11, 23, 4, 20], h = 5
Output: 30
Explanation: With only 5 hours for 5 piles, each pile must be finished in one hour, so k must be at least the largest pile.Input: piles = [30, 11, 23, 4, 20], h = 6
Output: 23Constraints
1 <= piles.length <= 10^4piles.length <= h <= 10^91 <= piles[i] <= 10^9
💡 Key Insight
The time needed, hours(k) = Σ ceil(piles[i] / k), only goes down as k goes up. So the speeds split into a "too slow" prefix and a "fast enough" suffix: F F F F T T T .... We want the first T, which is exactly what binary search on the answer finds. The answer lies in [1, max(piles)] because at speed max(piles) every pile takes one hour and h >= n.
Brute Force
TimeO(n · M) where M = max(piles)SpaceO(1).Intuition
Try every speed k = 1, 2, 3, ... and return the first one whose total hours fit in h. It is guaranteed to stop by k = max(piles).
Algorithm
- Let
maxPile = max(piles). - For
kfrom1tomaxPile:- Compute
hours = Σ ceil(p / k). - If
hours <= h, returnk.
- Compute
- Return
maxPile(unreachable in practice).
Visualization
piles = [3, 6, 7, 11], h = 8
k=1: 3+6+7+11 = 27 > 8 ✗
k=2: 2+3+4+6 = 15 > 8 ✗
k=3: 1+2+3+4 = 10 > 8 ✗
k=4: 1+2+2+3 = 8 ≤ 8 ✓ → return 4Code
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int maxPile = 0;
for (int p : piles) {
maxPile = Math.max(maxPile, p);
}
for (int k = 1; k <= maxPile; k++) {
if (hoursNeeded(piles, k) <= h) {
return k; // first speed that works is the minimum
}
}
return maxPile;
}
private long hoursNeeded(int[] piles, int k) {
long hours = 0; // can exceed int range when k is small
for (int p : piles) {
hours += (p + (long) k - 1) / k; // ceil(p / k)
}
return hours;
}
}Complexity
- Time: O(n · M) where
M = max(piles)— up to 10^9 speeds, each costing O(n). Times out. - Space: O(1).
Linear Scan from a Lower Bound
TimeO(n · M) worst caseSpaceO(1).Intuition
Even with no rounding waste, Koko must eat sum(piles) bananas in h hours, so k >= ceil(sum / h). Starting the scan there skips many hopeless speeds. It is often much faster in practice, but the worst case is still linear in the range.
Algorithm
- Compute
sum(aslong) andmaxPile. - Start at
k = max(1, ceil(sum / h)). - Increase
kuntilhoursNeeded(k) <= h, then returnk.
Visualization
piles = [3, 6, 7, 11], h = 8
sum = 27 → start at k = ceil(27 / 8) = 4
k=4: 8 hours ≤ 8 ✓ → return 4 (k = 1..3 skipped)Code
class Solution {
public int minEatingSpeed(int[] piles, int h) {
long sum = 0;
for (int p : piles) {
sum += p;
}
// no speed below ceil(sum / h) can possibly finish in time
int k = (int) Math.max(1, (sum + h - 1) / h);
while (hoursNeeded(piles, k) > h) {
k++;
}
return k;
}
private long hoursNeeded(int[] piles, int k) {
long hours = 0;
for (int p : piles) {
hours += (p + (long) k - 1) / k;
}
return hours;
}
}Complexity
- Time: O(n · M) worst case — rounding waste can force many increments (e.g. many piles of size
k + 1). - Space: O(1).
Binary Search on the AnswerOptimal
TimeO(n · log M)SpaceO(1).Intuition
hoursNeeded(k) <= h is a monotonic predicate: once a speed works, every faster speed works too. Binary search the range [1, maxPile] for the smallest speed where it becomes true. If mid works, the answer is mid or something smaller (hi = mid); otherwise it is strictly larger (lo = mid + 1).
Algorithm
lo = 1,hi = max(piles).- While
lo < hi:mid = lo + (hi - lo) / 2.- If
hoursNeeded(mid) <= h, sethi = mid; else setlo = mid + 1.
- Return
lo(now equal tohi).
Dry Run
flowchart TD
A["piles = [3,6,7,11], h = 8<br/>lo=1, hi=11"] --> B["mid=6: 1+1+2+2 = 6 hours"]
B -->|"6 ≤ 8, works"| C["hi=6"]
C --> D["mid=3: 1+2+3+4 = 10 hours"]
D -->|"10 > 8, too slow"| E["lo=4"]
E --> F["mid=5: 1+2+2+3 = 8 hours"]
F -->|"works"| G["hi=5"]
G --> H["mid=4: 1+2+2+3 = 8 hours"]
H -->|"works"| I["hi=4"]
I --> J["lo == hi == 4 → return 4"]Code
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int lo = 1;
int hi = 0;
for (int p : piles) {
hi = Math.max(hi, p); // speed max(piles) always works
}
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (hoursNeeded(piles, mid) <= h) {
hi = mid; // mid works; maybe something smaller does too
} else {
lo = mid + 1; // mid too slow; need strictly faster
}
}
return lo;
}
private long hoursNeeded(int[] piles, int k) {
long hours = 0; // use long: 10^4 piles * 10^9 hours overflows int
for (int p : piles) {
hours += (p + (long) k - 1) / k; // integer ceil(p / k)
}
return hours;
}
}Complexity
- Time: O(n · log M) —
log2(10^9) ≈ 30iterations, each summing overnpiles. - Space: O(1).
Complexity Comparison
| Approach | Time | Space |
|---|---|---|
| Brute Force | O(n · M) | O(1) |
| Linear Scan from Lower Bound | O(n · M) worst case | O(1) |
| Binary Search on Answer | O(n · log M) | O(1) |
Common Pitfalls
- Summing hours in an
int— withk = 1and huge piles the total reaches ~10^13 and overflows, making a too-slow speed look valid. - Using
p / kinstead of a ceiling: a pile of 7 at speed 4 takes 2 hours, not 1. Use(p + k - 1) / k(with alongto be safe) orMath.ceil. - Starting
loat0—midcan then be0and cause division by zero. - Mixing templates: with
while (lo < hi)you must usehi = mid(notmid - 1), otherwise you can skip over the answer. - Setting
hi = sum(piles)or10^9works but is wasteful;max(piles)is the tight upper bound.