Koko Eating Bananas

Medium Binary Search

Problem

Given an array piles where piles[i] is the number of bananas in the ith pile and an integer h representing hours, return the minimum integer k such that Koko can eat all bananas within h hours by eating k bananas per hour from one pile at a time.

  • 1 ≤ piles.length ≤ 10⁴
  • 1 ≤ piles[i] ≤ 10⁹
  • piles.length ≤ h ≤ 10⁹

Example

Input: piles = [3,6,7,11], h = 8
Output: 4

At speed k=4, Koko eats as follows: - Hour 1: Eat 4 bananas from pile 1 (3 bananas), finishes pile 1 in 1 hour. - Hour 2: Eat 4 bananas from pile 2 (6 bananas), 2 hours total. - Hour 3: Eat remaining 2 bananas from pile 2, 3 hours total. - Hour 4: Eat 4 bananas from pile 3 (7 bananas), 4 hours total. - Hour 5: Eat remaining 3 bananas from pile 3, 5 hours total. - Hour 6: Eat 4 bananas from pile 4 (11 bananas), 6 hours total. - Hour 7: Eat 4 bananas from pile 4, 7 hours total. - Hour 8: Eat remaining 3 bananas from pile 4, 8 hours total. This is the minimum speed to finish within 8 hours.

Approach

Straightforward Solution

A brute-force approach tries all speeds from 1 to max(piles), computing hours needed for each. This is O(n * max(piles)) and is infeasible for large inputs due to the large search space.

Core Observation

The minimum eating speed k lies between 1 and the maximum pile size. For any candidate speed k, the total hours needed to finish all piles can be computed by summing the ceiling of pile size divided by k. This total hours function is monotonically decreasing with respect to k.

Path to Optimal

Recognizing the monotonic relationship between speed and hours needed enables binary search on k. By checking midpoints and adjusting the search space based on whether the total hours exceed h, the algorithm efficiently narrows down the minimum feasible speed.

Optimal Approach

Use binary search between 1 and max(piles). For each mid speed k, compute total hours by summing ceil(pile/k) for all piles. If total hours <= h, update result and search left half to find smaller feasible speeds. Otherwise, search right half. Return the smallest k found.

Time

O(n log m)

Where n is the number of piles and m is the maximum pile size. Each binary search iteration takes O(n) to compute total hours, and binary search performs O(log m) iterations.

Space

O(1)

Only a few variables are used for pointers and counters; no additional data structures scale with input size.

Pattern Spotlight

Binary Search (Search Space Reduction on Monotonic Function)

When the problem asks for a minimum or maximum value satisfying a monotonic condition, transform the problem into a decision problem and use binary search over the value domain to efficiently find the optimal solution.

Solution

1import math
2
3class Solution:
4 def minEatingSpeed(self, piles: list[int], h: int) -> int:
5 left, right = 1, max(piles)
6 res = right
7
8 while left <= right:
9 k = (left + right) // 2
10 hours = 0
11 for p in piles:
12 hours += math.ceil(p / k)
13
14 if hours <= h:
15 res = min(res, k)
16 right = k - 1
17 else:
18 left = k + 1
19
20 return res

Step-by-Step Solution

1

Initialize Binary Search Boundaries and Result

5left, right = 1, max(piles)
6res = right

Objective

To set the initial search space for the eating speed between 1 and the largest pile size, and initialize the result to the upper bound.

Key Insight

The minimum speed cannot be less than 1 and cannot exceed the largest pile size, as eating faster than the largest pile is unnecessary. Initializing the result to the upper bound ensures a valid starting point for minimization.

Interview Quick-Check

Core Logic

Setting left to 1 and right to max(piles) defines the search space for the binary search over possible speeds.

State & Boundaries

Initializing result to right ensures that if no smaller speed satisfies the condition, the maximum pile size is returned.

2

Perform Binary Search to Find Minimum Feasible Speed

8while left <= right:
9 k = (left + right) // 2
10 hours = 0
11 for p in piles:
12 hours += math.ceil(p / k)
14 if hours <= h:
15 res = min(res, k)
16 right = k - 1
17 else:
18 left = k + 1

Objective

To iteratively narrow down the search space by checking the feasibility of the mid speed and adjusting boundaries accordingly.

Key Insight

By computing the total hours needed at speed k and comparing it to h, the algorithm decides whether to search for smaller speeds (if feasible) or larger speeds (if not). This exploits the monotonicity of the hours function to efficiently converge on the minimal valid speed.

Interview Quick-Check

Core Logic

Calculate total hours for mid speed by summing ceil(pile/k) for all piles; if total hours <= h, update result and search left half; else search right half.

State & Boundaries

The loop continues while left <= right, ensuring all candidate speeds are considered.

Common Pitfalls & Bugs

Using integer division instead of ceiling can underestimate hours, leading to incorrect feasibility checks.

Complexity

Each iteration takes O(n) time to compute hours, and binary search performs O(log m) iterations, resulting in O(n log m) total time.

3

Return the Minimum Eating Speed Found

20return res

Objective

To output the smallest speed k that allows Koko to finish eating within h hours.

Key Insight

After binary search completes, the stored result is guaranteed to be the minimal feasible speed due to the search narrowing and updates during the loop.

Interview Quick-Check

Core Logic

Returning the result after the binary search loop ensures the minimal speed satisfying the time constraint is output.

Line Analysis

This solution has 1 Critical line interviewers watch for.

Line 12 Critical
hours += math.ceil(p / k)

Add the ceiling of pile divided by k to the total hours.

This line is critical because it correctly models the time cost per pile using ceiling division, which is essential for accurate feasibility checks; using floor division would underestimate hours and break correctness.

Line 5 Core
left, right = 1, max(piles)

Initialize the binary search boundaries: left at 1 and right at the maximum pile size.

The minimum eating speed must be at least 1 and cannot exceed the largest pile, as eating faster than the largest pile is unnecessary and does not reduce hours further.

Line 14 Core
if hours <= h:

Check if total hours needed is within the allowed time h.

This condition determines if the current speed k is feasible, guiding the binary search direction.

Line 20 Core
return res

Return the minimal feasible eating speed found.

After binary search completes, the stored result is guaranteed to be the smallest speed that allows finishing within h hours.

Line 9 Core
k = (left + right) // 2

Calculate the midpoint speed k as the candidate eating speed.

Choosing the midpoint divides the search space in half, enabling logarithmic convergence of the binary search.

Line 11 Core
for p in piles:

Iterate over each pile to calculate hours needed at speed k.

Each pile contributes ceil(pile/k) hours, representing the number of full hours needed to finish that pile at speed k.

Line 15 Core
res = min(res, k)

Update the result to the smaller feasible speed k.

Recording the minimal feasible speed found so far ensures the final answer is the smallest speed satisfying the time constraint.

Line 16 Core
right = k - 1

Adjust the right boundary to search for smaller speeds.

Since k is feasible, searching the left half may find an even smaller feasible speed, refining the solution.

Line 17 Core
else:

Handle the case where total hours exceed h, making k infeasible.

If k is too slow, increase the left boundary to search for faster speeds that reduce total hours.

Line 18 Core
left = k + 1

Adjust the left boundary to search for larger speeds.

Increasing left discards infeasible speeds, focusing the search on speeds that might satisfy the time constraint.

Line 6 Standard
res = right

Initialize the result variable to the upper boundary of the search space.

Setting result to right ensures a valid initial answer that will be updated to smaller feasible speeds during the search.

Line 8 Standard
while left <= right:

Start the binary search loop, continuing while the search space is valid.

The condition left <= right ensures all candidate speeds in the range are considered, enabling convergence to the minimal feasible speed.

Line 10 Standard
hours = 0

Initialize the hours counter for the current candidate speed.

Resetting hours to zero prepares to accumulate the total hours needed to finish all piles at speed k.

Test Your Understanding

Why is binary search applicable to the eating speed k in this problem?

Because the total hours needed to finish all piles is a monotonically decreasing function of k, allowing the use of binary search to find the minimum k that satisfies the time constraint.

Related Problems

Binary Search pattern

Don't just read it. Drill it.

Reconstruct Koko Eating Bananas from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Koko Eating Bananas drill

or drill a free problem