AlgoMaster Logo

My Calendar II

Last Updated: March 29, 2026

medium
4 min read

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).

The key is understanding 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.

Notice that 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.

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 huge, so we cannot use a fixed-size array indexed by time. We need approaches that work with sparse intervals.
  • At most 1000 calls to book -> With only 1000 operations, even an O(n^2) approach per call runs in about a million operations total. That is perfectly fine.

Approach 1: Brute Force (Two Lists)

Intuition

The most natural way to think about this: 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).

This is essentially a two-pass approach: first pass checks for rejection, second pass updates the state.

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

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

Code

This approach works correctly but the double-booked list can grow quadratically. What if instead of tracking individual overlaps, we used a single data structure that tells us exactly how many events are active at any point in time?

Approach 2: Line Sweep with Sorted Map

Intuition

Here is a fundamentally different way to think about the problem. Instead of tracking which intervals overlap with which, imagine a timeline. Each booking adds 1 to the "event count" at its start time and subtracts 1 at its end time. If we process these events in sorted order and maintain a running count, we know exactly how many events are active at every moment.

A triple booking occurs when the running count reaches 3. So the algorithm is: tentatively add the new booking's events to the timeline, sweep through the events in order, and check if the count ever hits 3. If it does, undo the addition and return false. If it never does, keep the addition and return true.

This is the classic line sweep (or difference array) technique, but since the time range is up to 10^9, we use a sorted map (TreeMap in Java, SortedDict in Python) instead of an array. The map only stores time points where events start or end, keeping it sparse and efficient.

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