Binary search on the answer, not the array

There is no array to search here. The trick is that feasibility is monotonic: if a capacity works, every larger one works too. So the answers form a sorted sequence of no-no-no-yes-yes, and binary search finds the boundary — with an O(n) feasibility check in place of an array lookup.

Overview

The question, and what it is testing

There is no array to search here. The trick is that feasibility is monotonic: if a capacity works, every larger one works too. So the answers form a sorted sequence of no-no-no-yes-yes, and binary search finds the boundary — with an O(n) feasibility check in place of an array lookup.

Binary searchCoding problemMedium

Step through it

What to watch

  • The thing being searched is a range of answers, not the input.
  • Each probe costs a full O(n) pass — the feasibility check.
  • The predicate flips exactly once, which is what makes it searchable.

Say this out loud

"I binary search the answer rather than an array. The predicate is 'can this capacity finish in D days', which I can check in one pass, and it is monotonic - if a capacity works, anything bigger works. So the feasible capacities are a suffix, and I search for the first one. Bounds are max weight to total weight, so it is O(n log sum)."

Binary search on the answer, not the array

Given package weights and D days, what is the smallest ship capacity that delivers them all in time?

Run it

The probe table: five probes over a range of forty-six candidates.

1Python
Output

Monotonicity is what licenses the search, so check it rather than assume it.

2Python
Output

The same shape on a different question, to show it is a pattern and not a trick.

3Python
Output

Recognising it

The tell is a question of the form "what is the smallest X such that something is possible" — smallest capacity, minimum speed, fewest days, largest minimum distance. There is no sorted array in the input, which is why the shape gets missed.

What is sorted is the answer space. If you can write a function feasible(x) that is False for every x below the answer and True for every x at or above it, then the sequence feasible(lo) ... feasible(hi) is sorted — and finding where it flips is exactly binary search.

The three things to establish before writing the loop

The predicate. Here, "can capacity c deliver everything in D days?" Greedily load until the next package would overflow, then start a new day. One pass, O(n), and greedy is optimal for it because leaving room spare can never reduce the day count.

Monotonicity. A bigger ship cannot need more days. State it — this is the step that makes the search valid, and an interviewer will ask why you are allowed to binary search something with no array in it.

The bounds. lo = max(weights), because a package must fit in one trip. hi = sum(weights), because that always finishes in one day. Getting the low bound wrong — starting at 1, or at 0 — produces a search over capacities that can never work, and the loop returns an infeasible answer rather than looping forever, which is worse.

Cost, and the shape of it

O(n log(sum − max)): a logarithmic number of probes, each costing a linear pass. Note that the log is over the magnitude of the answer range rather than the size of the input, which is unusual and worth saying — it means the complexity depends on the numbers, not just on how many there are.

The editor below prints the probe table. Ten packages give a range of 46 candidate capacities and the search settles it in five probes.

The same shape, four other questions

Koko eating bananas — smallest eating speed to finish in H hours. Identical, with ceil(pile / speed) as the per-pile cost.

Split an array into k subarrays minimising the largest sum. The predicate is "can we do it with all parts at most m", and it is the same greedy pass.

Aggressive cows / maximum minimum distance — place k items as far apart as possible. The monotonicity flips direction: large distances are infeasible, so you search for the last True instead of the first.

The square root of an integer, or any inverse of a monotonic function. Once the pattern is visible, "is there a monotonic predicate here?" becomes a routine question to ask of any minimisation problem.

What to say out loud

I binary search the answer rather than an array. The predicate is 'can this capacity finish in D days', which I can check in one pass, and it is monotonic - if a capacity works, anything bigger works. So the feasible capacities are a suffix, and I search for the first one. Bounds are max weight to total weight, so it is O(n log sum).

Edge cases to raise

Volunteering these is most of what separates a correct answer from a good one.

Verify the monotonicity. If the predicate flips more than once, binary search returns a boundary that means nothing. Printing it either side of the answer is cheap insurance.

The bounds must bracket the answer. Too low and the search can return an infeasible value; too high only costs a probe or two. When in doubt, err high.

The log is over the magnitude of the answer range, not the input length - so the complexity depends on the size of the numbers. Worth stating, because it is unusual.

The follow-ups interviewers ask

"Koko eating bananas." Identical: smallest speed such that the total hours fit, with ceil(pile / speed) per pile. If you can see that these are the same question, you have the pattern.

"Split an array into k parts minimising the largest sum." The predicate is 'can it be done with every part at most m', checked greedily in one pass. Same search, same monotonicity.

"Maximise the minimum distance when placing k items." The monotonicity reverses - large distances are infeasible - so you search for the last feasible value instead of the first. Recognising that the direction flips is the test.

Common wrong answers

"There is nothing sorted, so binary search does not apply." The answer space is sorted, even when the input is not. That reframing is the entire question.

"Start the range at 1." Sometimes harmless and sometimes not. The lower bound must be feasible-or-below by construction; for capacity that is max(weights), because a single package has to fit.

"Check feasibility by trying all arrangements." The greedy pass is O(n) and optimal here. Searching arrangements makes each probe exponential and throws the whole benefit away.

Recap in one screen

  • The thing being searched is a range of answers, not the input.
  • Each probe costs a full O(n) pass - the feasibility check.
  • The predicate flips exactly once, which is what makes it searchable.
  • Worth trying: Set lo = 1 instead of max(weights) and look at the answer. It is still correct here - work out why, and then find an input where it would not be.
  • Worth trying: Change the question to "the largest minimum gap when placing k items". The monotonicity reverses, so you search for the last feasible value instead of the first.

How the code works

The probe table, the monotonicity that licenses the search, and the same technique applied to a second problem to show it is a shape rather than a trick.

How the code works

  1. lo, hi = max(weights), sum(weights)The bounds are part of the answer. Below max no capacity can work at all, and sum always finishes in one day, so the boundary is guaranteed to be inside.
  2. if load + w > capacity: days += 1The feasibility check, greedy and O(n). Greedy is optimal here because leaving space unused can never reduce the number of days.
  3. hi = midA feasible capacity is kept as a candidate, exactly like the lower-bound search — you are looking for the first True, not for any True.
  4. for cap in range(answer - 3, answer + 3)Printing the predicate either side of the boundary is how you check monotonicity rather than assuming it. A predicate that flips more than once cannot be binary searched.

Change one thing

  • Set lo = 1 instead of max(weights) and look at the answer. It is still correct here — work out why, and then find an input where it would not be.
  • Change the question to "the largest minimum gap when placing k items". The monotonicity reverses, so you search for the last feasible value instead of the first.

Where this runs

Real CPython, compiled to WebAssembly and running on your own machine — nothing is uploaded. The first run takes a few seconds while the interpreter downloads; after that it is immediate. Need more room, or want to paste your own attempt? Use the Python compiler.

Check yourself

0 of 4

Answer without scrolling back up.

  1. What is being binary searched?

  2. What property must the predicate have?

  3. What is the complexity?

  4. Why is lo initialised to max(weights)?

Cheat sheet

Binary search on the answer, not the array

There is no array to search here. The trick is that feasibility is monotonic: if a capacity works, every larger one works too. So the answers form a sorted sequence of no-no-no-yes-yes, and binary search finds the boundary — with an O(n) feasibility check in place of an array lookup.

INTERVIEW · vizlearn.in/interview/binary-search-on-the-answer.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.