AlgoMaster Logo

My Calendar II

mediumFrequency6 min readUpdated June 23, 2026

Understanding the Problem

We are building a calendar that accepts bookings one at a time. Each booking is a half-open interval [start, end). We allow double bookings (two events overlapping), but we must reject any booking that would create a triple booking (three events all overlapping at the same moment).

Consider what "triple booking" means concretely. If we already have bookings [10, 20) and [10, 40), the overlap region [10, 20) is double-booked. If a new booking [5, 15) comes in, the region [10, 15) would overlap with both existing bookings, creating a triple booking, so we reject it.

The intervals are half-open: [start, end) means start is included but end is not. So [10, 20) and [20, 30) do not overlap, since 20 belongs to the second interval but not the first. This boundary rule matters for the overlap test below, where we use strict < rather than <=.

The core challenge is efficiently tracking where double bookings exist, so we can quickly check whether a new event would turn any double booking into a triple booking.

Key Constraints:

  • 0 <= start < end <= 10^9 -> The time range is too large to index a fixed-size array by time. We need approaches that work with sparse intervals.
  • At most 1000 calls to book -> An O(n^2) total approach runs in about a million operations, which is fast enough. This rules out needing a balanced-tree or segment-tree solution for performance, though we cover the line sweep below because it generalizes better.

Approach 1: Brute Force (Two Lists)

Intuition

Keep one list of all accepted bookings and a separate list of all double-booked regions. When a new event [start, end) comes in, we do two checks:

  1. Does the new event overlap with any region that is already double-booked? If yes, adding this event would create a triple booking, so reject it.
  2. If no triple booking would occur, compute the overlap between the new event and every existing single booking. Each such overlap becomes a new double-booked region. Then add the new event to the bookings list.

The overlap between two half-open intervals [s1, e1) and [s2, e2) is [max(s1, s2), min(e1, e2)). This overlap is non-empty only when max(s1, s2) < min(e1, e2).

The order of the two passes is what keeps this correct. We test against doubleBooked before adding any new regions, so the new event is never compared against an overlap it just produced with itself. Each region in doubleBooked came from a distinct pair of earlier events, so overlapping any of them means three events share a point.

Algorithm

  1. Maintain two lists: bookings (all accepted events) and doubleBooked (all double-booked regions).
  2. When book(start, end) is called:
    • For each interval [ds, de) in doubleBooked, check if max(start, ds) < min(end, de). If any such overlap exists, return false.
    • For each interval [bs, be) in bookings, compute the overlap [max(start, bs), min(end, be)). If this overlap is non-empty, add it to doubleBooked.
    • Add [start, end) to bookings.
    • Return true.

Example Walkthrough

bookings
1book(10,20): doubleBooked empty, bookings empty. Add [10,20).
[10, 20]
1020
doubleBooked
1No double bookings yet
[]
1/6

Code

The next approach drops the separate overlap list. Instead of tracking which intervals overlap which, it counts how many events are active at each point in time and rejects a booking when that count would reach three.

Approach 2: Line Sweep with Sorted Map

Intuition

Each booking adds 1 to the active-event count at its start time and subtracts 1 at its end time. Process these changes in sorted time order while maintaining a running count, and that count equals the number of events active at every moment.

A triple booking occurs when the running count reaches 3. The algorithm tentatively adds the new booking's two deltas to the timeline, sweeps through the entries in sorted order, and checks whether the count ever hits 3. If it does, it undoes the addition and returns false. Otherwise it keeps the addition and returns true.

This is the line sweep (difference array) technique. Since the time range is up to 10^9, we store the deltas in a map keyed by time point (TreeMap in Java, a plain dict iterated in sorted order in Python) instead of an array. The map holds only the time points where some event starts or ends, so its size stays proportional to the number of bookings.

Algorithm

  1. Maintain a sorted map timeline where each key is a time point and each value is the net change in active events at that time.
  2. When book(start, end) is called:
    • Add +1 to timeline[start] and -1 to timeline[end].
    • Sweep through the map in sorted key order, accumulating a running count.
    • If the running count ever reaches 3, undo the changes (subtract 1 from timeline[start], add 1 to timeline[end]), clean up zero entries, and return false.
    • If the sweep completes without hitting 3, return true.

Example Walkthrough

1book(10,20): Add +1 at 10, -1 at 20. Sweep: max count=1. Accept.
10
:
1
20
:
-1
1/6

Code