Fewest bookings to cancel so none clash

Greedy choices and intervals Mid-level Python 3.13+ Kotlin 2.4+ Reviewed By Best Latest Apps

Pick the fix

Bookings [1, 10), [2, 3), [3, 4), [4, 5). Keeping by earliest start keeps only [1, 10). What is the better rule?

Options

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A Keep by earliest end time

Right: it keeps [2, 3), [3, 4) and [4, 5) and cancels one booking.

Ending early never removes an option that a later-ending choice would have kept, which is the whole proof.

Why not the others?
  • B Keep the longest bookings. Long bookings block the most others.
  • C Keep by latest start time. That works only when mirrored with a matching proof; earliest end is the standard rule.

30-second answer

Keeping the most bookings is the same as cancelling the fewest. Sort by end time and keep each booking that starts no earlier than the last kept one ends; cancel the rest. Finishing earliest leaves the most room for what follows. O(n log n) time, O(n) space for the sorted copy.

Practise saying it · 45 s. No recording, just a timer

Explain the fewest-cancellations greedy.

Say your answer out loud, then check which of these points you covered:

  • Sort by end
  • Keep if it starts after the last kept end
  • Exchange argument

Deep dive

Sorting by end is the template for 'keep the most intervals that do not overlap'.

Sorting by start fails: one long early booking blocks many short ones.

The exchange argument: swap the first booking of any best answer for the earliest-ending one; nothing later clashes.

Python solution Ran on Python 3.13.5 · mypy --strict
Python 3.13
def fewest_cancels(bookings: list[list[int]]) -> int:
    kept_end = None  # end time of the last booking kept
    cancels = 0
    for start, end in sorted(bookings, key=lambda b: b[1]):
        if kept_end is not None and start < kept_end:
            cancels += 1  # clashes: drop it, keep the earlier finish
        else:
            kept_end = end
    return cancels

Kotlin Ran on Kotlin 2.4.21 · JDK 21.0.12.1
Kotlin 2.4
fun fewestCancels(bookings: List<List<Int>>): Int {
    var keptEnd: Int? = null // end time of the last booking kept
    var cancels = 0
    for (b in bookings.sortedBy { it[1] }) {
        val end = keptEnd
        if (end != null && b[0] < end) cancels++ else keptEnd = b[1]
    }
    return cancels
}

Common mistakes

  • Sorting by start or by length.
  • Treating touching bookings as clashing when the question says they do not.

Senior take & follow-ups

State the exchange argument in one sentence; it is the proof interviewers want.

Follow-up questions

Think of your answer first, then open the hint.

  1. How many arrows hit a set of balloons on a line?

    Show hint

    The same sort by end; one arrow per kept interval, with touching counted as shared.

  2. What if bookings have values?

    Show hint

    Weighted interval scheduling: DP over bookings sorted by end, with binary search for the last compatible one.

What each operation costs: Merge Sort · Quick Sort

From the author

DSA Master - Data & Algorithms

Free on iPhone and iPad: watch 50+ data structures and algorithms step through animations, run the code in the playground, and try the daily challenge.

Suggest a fix if something here is wrong or out of date.