Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 9 – Binary search

Maximise the minimum distance

Place k items at given positions so that the closest two are as far apart as possible: sort, check a gap greedily and binary search the widest gap.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 5 of 25 minutes free
Free: The problem: spreading out fast chargers. With a pass: the other 10 sections and 3 interview answers, about 20 minutes.

What you will learn

  • Choose k of n given positions so that the smallest gap between them is as large as possible
  • Write the greedy feasibility check after sorting, and prove that it is optimal for a fixed gap
  • Search for the last value at which a condition holds without an endless loop
  • Rebuild the placement as well as the distance from the search's answer

Before you start

What you’ll learn · Before you start

What you will learn

  • Choose k of n given positions so that the smallest gap between them is as large as possible
  • Write the greedy feasibility check after sorting, and prove that it is optimal for a fixed gap
  • Search for the last value at which a condition holds without an endless loop
  • Rebuild the placement as well as the distance from the search's answer

Before you start

On this page

Maximising the minimum distance means choosing k of n given positions so that the two closest chosen ones are as far apart as possible. The search does not try the choices. It guesses a gap d and asks whether k positions can be chosen with every gap at least d: after sorting, one walk from the left answers that, taking each position that is far enough from the last one taken. A smaller gap never makes placing harder, so “k fit with gap d” is true up to some d and false beyond it, and the last d that works can be binary searched. The problem below is an original take on the classic known as aggressive cows; a timed run shows what trying every choice would cost, and a short note names the signal.

The problem: spreading out fast chargers

The problem

A highway authority has n candidate sites for fast chargers at kilometre markers sites[i] along one highway. The list is in the order the sites were surveyed, not sorted, and two sites may share a marker. It will build exactly k chargers at k different sites and wants them spread out: return the largest possible distance between the two closest chargers. Limits: 2 ≤ k ≤ n ≤ 10⁵ and 0 ≤ sites[i] ≤ 10⁹.

  1. sites = [48, 3, 95, 20, 61, 77, 12] and k = 4 give 18: chargers at 3, 48, 77 and 95, whose gaps are 45, 29 and 18.
  2. The same sites with k = 2 give 92, the two ends of the highway.
  3. sites = [5, 5, 5] and k = 2 give 0: two different sites at the same marker.

The brute force: try every choice of k sites

Sort the sites, try each of the C(n, k) choices of k of them, take each choice’s smallest gap, and keep the widest. It is a correct judge for small inputs, and the number of choices explodes:

Trying every choice of sites, and how the number of choices grows Python · brute/every_choice.py
"""The widest smallest gap, by trying every choice of k sites: the brute force, with its choices counted."""
import math
import time
from itertools import combinations


def widest_by_trying(sites, k):
    ordered = sorted(sites)
    best, choices = None, 0
    for chosen in combinations(ordered, k):  # each choice comes out in sorted order
        gap = min(b - a for a, b in zip(chosen, chosen[1:]))
        best = gap if best is None else max(best, gap)
        choices += 1
    return best, choices


for sites, k in [([48, 3, 95, 20, 61, 77, 12], 4), ([48, 3, 95, 20, 61, 77, 12], 2), ([5, 5, 5], 2)]:
    best, choices = widest_by_trying(sites, k)
    print(f"k = {k}: {best} after {choices:,} choices")

sites = [(37 * i) % 1_000 for i in range(26)]
start = time.perf_counter()
best, choices = widest_by_trying(sites, 7)
per_choice = (time.perf_counter() - start) / choices
print(f"26 sites, 7 chargers: {choices:,} choices, about {per_choice * 1e6:.1f} microseconds each")


def duration(seconds):
    for unit, size in (("year", 365 * 86_400), ("day", 86_400), ("hour", 3_600), ("minute", 60), ("second", 1)):
        if seconds >= size or unit == "second":
            value = round(seconds / size)
            return f"about {value:,} {unit}{'' if value == 1 else 's'}"


for n, k in [(40, 10), (100, 10), (100_000, 50)]:
    count = math.comb(n, k)
    shown = f"{count:,} choices" if count < 10**15 else f"{len(str(count))}-digit number of choices"
    print(f"{n:,} sites, {k} chargers: {shown}, {duration(count * per_choice) if count < 10**30 else 'far longer than the age of the universe'}")

Output

k = 4: 18 after 35 choices
k = 2: 92 after 21 choices
k = 2: 0 after 3 choices
26 sites, 7 chargers: 657,800 choices, about 1.3 microseconds each
40 sites, 10 chargers: 847,660,528 choices, about 18 minutes
100 sites, 10 chargers: 17,310,309,456,440 choices, about 251 days
100,000 sites, 50 chargers: 186-digit number of choices, far longer than the age of the universe

This output changes from run to run: the times depend on the computer and on what else it is doing

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 every_choice.py

At a microsecond or more per choice, 40 sites and 10 chargers take minutes, 100 sites take months, and the stated limits are out of reach.

Permutation & Combination Calculator Count C(n, k), the ways to choose k charger sites out of n, for sizes of your own.

What to notice

Fix a gap d. If k chargers fit with every gap at least d, the same chargers have every gap at least any smaller d, so “k fit with gap d” holds up to some d and fails beyond it: search for the last d that works. For one d, sort the sites, take the first, and walk along taking each site that is at least d past the last one taken; k fit when the walk takes at least k.

Sites at 3, 12, 20, 48, 61, 77 and 95: with gaps of at least 18 four chargers fit; with at least 19 only three.Gap at least 18: chargers at 3, 48, 77 and 954529183122048617795Gap at least 19: 95 is too close to 77, so 3 fit45293122048617795Whatever fits a gap of 19 also fits 18: the last gapat which 4 chargers fit is the answer

The greedy placement at two gaps

Text description of the diagram

The diagram shows the highway twice as a line with seven candidate sites at kilometre markers 3, 12, 20, 48, 61, 77 and 95. Chosen sites are filled; the others are hollow.

In the first row every gap must be at least 18. Starting at the first site and taking each next site that is at least 18 further, the chargers go to 3, 48, 77 and 95, with gaps of 45, 29 and 18: four chargers fit, and the smallest gap is 18.

In the second row every gap must be at least 19. The same walk takes 3, 48 and 77, but 95 is only 18 past 77, so only three chargers fit.

The note at the bottom says that whatever fits a gap of 19 also fits a gap of 18, so the answer is the last gap at which four chargers fit.

The widest gap by halving, step by step Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Why taking the leftmost site first is optimal: the proof
  • The last true, not the first: two ways to write it
  • The search in five languages
  • Forgetting to sort, reproduced
  • Returning the chargers as well as the distance
  • Edge cases, variants and follow-ups
    • Edge cases and common bugs
    • Variants, with answers
    • Follow-up questions, answered
  • Practise: the largest smallest share of a reading list
    • The largest smallest share of a reading list
  • Check yourself
  • Interview questions
    • To place k chargers at given markers with every gap at least d, you take the leftmost site and then each next site at least d further on. Prove that this places the most chargers possible, and explain why the sites must be sorted first.
    • The highway becomes a ring road of length L: markers run from 0 to L − 1, and the gap from the last charger back to the first wraps round. How do you find the widest smallest gap for k chargers, and what does it cost for n = 10⁵ sites?
    • Your search for the widest gap passes the samples but hangs on a hidden test with two chargers. After a quick fix it returns 93 instead of 92 on another test. Explain both bugs from the loop's invariant, and write the loop so that neither can happen.
  • Key takeaways

5 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 22 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.