Binary Search
Advanced 11 min read

Binary search on the answer

In one line

When you can quickly check whether a candidate answer is good enough, and goodness only flips once as the candidate grows, binary search over the range of possible answers instead of over an array.

What it is

Sometimes there is no sorted array to search, but the answer is a number in a known range: the minimum speed, the smallest capacity, the largest minimum distance. If you can write a function feasible(x) that says yes or no, and the answers look like no, no, no, yes, yes, yes as x grows (monotonic), then the smallest feasible x can be found by binary search.

The cost is the number of halvings, about log2 of the range size, times the cost of one feasibility check. A range of 1 to 1,000,000,000 needs only about 30 checks. The skill is in recognising the pattern and writing a correct greedy check.

Why interviewers ask

This is a favourite interview pattern because it is not obvious: the problem talks about days, speeds or distances, not searching. Interviewers look for whether you notice the monotonic property, pick tight bounds, write an O(n) feasibility check, and reason about the total O(n log range) time. It separates candidates who memorised binary search from those who understand it.

How it works

  1. 1Identify the answer variable x and its possible range [lo, hi]. For example, eating speed is between 1 and the largest pile.
  2. 2Write feasible(x): can the task be done with value x? It is usually a simple greedy pass over the input in O(n).
  3. 3Check monotonicity: if x works, does every larger x also work (or every smaller x, for maximise problems)? If not, binary search does not apply.
  4. 4Search for the first feasible x: while lo < hi, mid = (lo + hi) // 2; if feasible(mid) then hi = mid else lo = mid + 1. The answer is lo.
  5. 5For 'maximise the minimum' problems the predicate flips the other way (yes, yes, no, no); search for the last yes with mid = (lo + hi + 1) // 2, lo = mid on yes and hi = mid - 1 on no, so the loop always moves.
  6. 6Total time is O(cost of feasible x log(hi - lo)).

Worked example · Koko eating bananas: piles [3, 6, 7, 11], h = 8 hours

  1. At speed k, a pile p takes ceil(p / k) hours. Faster speeds never take longer, so feasibility is monotonic. Range: lo = 1, hi = 11 (the largest pile).
  2. mid = 6: hours = 1 + 1 + 2 + 2 = 6 <= 8, feasible, so hi = 6.
  3. lo = 1, hi = 6, mid = 3: hours = 1 + 2 + 3 + 4 = 10 > 8, not feasible, so lo = 4.
  4. lo = 4, hi = 6, mid = 5: hours = 1 + 2 + 2 + 3 = 8 <= 8, feasible, so hi = 5.
  5. lo = 4, hi = 5, mid = 4: hours = 1 + 2 + 2 + 3 = 8 <= 8, feasible, so hi = 4. lo == hi == 4: the minimum speed is 4. Check: speed 3 needs 10 hours, too many.

Picture it

Finding the cheapest phone plan that covers your usage: you do not need to try every plan, you try a middle one. If it is enough, anything bigger is too, so you look cheaper; if it is not enough, you look more expensive. A few tries and you have the cheapest plan that works.

Reference code

def min_eating_speed(piles, h):
    """Smallest speed k (bananas per hour) that finishes all piles within h hours."""
    def hours_needed(k):
        return sum((p + k - 1) // k for p in piles)   # ceil(p / k) per pile

    lo, hi = 1, max(piles)             # the answer is somewhere in [1, max pile]
    while lo < hi:
        mid = (lo + hi) // 2
        if hours_needed(mid) <= h:     # feasible: try to go slower
            hi = mid
        else:                          # too slow: must eat faster
            lo = mid + 1
    return lo                          # lo == hi == smallest feasible speed

hours_needed is the feasibility check, O(n). Faster speeds never need more hours, so feasibility flips once from false to true, and the first-true template finds the minimum. (p + k - 1) // k is integer ceiling. Total time O(n log M) where M is the largest pile; space O(1).

Key patterns

Minimise a rate or capacity

When: Minimum speed, capacity or bandwidth that finishes within a limit (Koko bananas, ship packages in D days)

How: Range [max item or 1, total or max]; feasible = greedy simulation; find the first true.

Minimise the largest part

When: Split an array into k parts to minimise the largest part sum

How: Guess the max sum S; greedily cut whenever a part would exceed S; feasible if parts <= k.

Maximise the minimum gap

When: Place k items (cows, routers, balls) as far apart as possible

How: Guess distance d; greedily place each item at the first position >= last + d; feasible if k fit. Search for the last true.

Kth smallest by counting

When: Kth smallest in a multiplication table or a sorted matrix

How: Guess value v; count how many entries are <= v; find the first v whose count is >= k.

Real-valued answers

When: Square root, minimum time with fractional values

How: Binary search on floats for a fixed number of iterations (for example 60 to 100) or until hi - lo < epsilon.

Real-world use

git bisect

git bisect searches commit history for the first bad commit: 'is this commit bad?' is the feasibility check, and it is monotonic once a bug is introduced. About 10 builds cover 1,000 commits.

Load and capacity testing

Performance teams find the maximum requests per second a service handles within its latency target by running load tests at a middle rate and halving the range, instead of stepping up slowly through every rate.

Video encoding to a target size

Encoding pipelines often binary search a quality setting, such as x264's CRF value, to find the best quality whose output still fits a size or bitrate budget, encoding only a handful of times.

Numerical methods

Bisection finds the root of a continuous function, or a square root to a set precision, by halving an interval where the function changes sign; it is simple and guaranteed to converge.

In a project

A warehouse must ship 10,000 parcels in a fixed order within 5 days. The team needs the smallest truck capacity that makes it, because larger trucks cost more per day.

  1. Answer variable: capacity C. Lower bound: the heaviest parcel (otherwise it never fits). Upper bound: the total weight (everything in one day).
  2. Feasibility: greedily load parcels in order, starting a new day when the next one does not fit; feasible if it needs <= 5 days. This is O(n).
  3. Monotonic: if capacity C works, any larger capacity also works, so binary search for the first feasible C.
  4. Report the answer with the loading plan for that capacity, and test small cases by brute force to confirm.

Impact: The answer comes from about 30 greedy passes instead of trying every capacity up to the total weight, runs in milliseconds, and lets the team pick the cheapest truck size that still meets the 5-day promise.

Trade-offs

Use it when

  • The problem asks for a minimum or maximum value that satisfies a condition
  • You can check a candidate answer quickly, usually with an O(n) greedy pass
  • Feasibility is monotonic in the candidate value
  • The answer range is large, so trying every value would be too slow

Avoid it when

  • Feasibility is not monotonic (a larger value can fail after a smaller one succeeds)
  • A direct formula or a single greedy pass gives the answer
  • The check itself is very expensive and a smarter direct algorithm exists (for example dynamic programming over a small range)

Cost: O(C x log R) where C is the cost of one feasibility check and R is the size of the answer range; usually O(n log R) with O(1) extra space.

Common mistakes

  • Not stating why feasibility is monotonic; without it the search is invalid.
  • Choosing bounds that exclude the answer, such as lo = 0 for a speed (division by zero) or hi smaller than the largest item.
  • Using mid = (lo + hi) // 2 with lo = mid for a 'last true' search, which loops forever when hi = lo + 1; round up instead.
  • Writing a feasibility check that is wrong at the boundaries, such as using < h instead of <= h hours.
  • Using floating-point division for ceil, which can give off-by-one errors; use (p + k - 1) // k.

Interview questions

Quick revision

  • Pattern: minimise or maximise x subject to feasible(x), with feasible monotonic.
  • First true: if feasible(mid) then hi = mid else lo = mid + 1.
  • Last true: mid = (lo + hi + 1) // 2; if feasible(mid) then lo = mid else hi = mid - 1.
  • Bounds must contain the answer: often [max item, sum] or [1, max].
  • Feasibility check is usually an O(n) greedy pass.
  • Total: O(n log range). Range 1e9 needs about 30 checks.
  • Integer ceil: (p + k - 1) // k.
  • Floats: iterate a fixed number of times or until hi - lo < eps.

Check yourself

1. What property must feasible(x) have for binary search on the answer to work?

2. For 'ship packages within D days', what is the correct lower bound for capacity?

3. Piles [3, 6, 7, 11] and h = 8. Is speed 3 feasible?

4. In a 'last true' search using lo = mid when feasible, why compute mid = (lo + hi + 1) // 2?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free