Builds on Merge Intervals, which you haven’t finished yet.
Sweep Line (Line Sweep)
You're given an array of meeting time intervals intervals, each entry [start_i, end_i]. Figure out the smallest number of conference rooms that lets every meeting happen -- no room may host two meetings whose times actually overlap (a meeting ending at end_i and another starting at start_j don't overlap once end_i <= start_j).
[0,30] overlaps both other meetings, but those two do not overlap each other, so 2 rooms are enough.Sweep Line
events (start: +1, end: -1) ➔ sort time with end before start ➔ max active count| 1 | def min_meeting_rooms(intervals: list[list[int]]) -> int: |
| 2 | """Sweep-Line Event Processing: minimum conference rooms required. |
| 3 | Time: O(N log N) | Space: O(N) |
| 4 | """ |
| 5 | # 1. Turn Each Interval into Two Point Events: |
| 6 | # Only the running count of meetings in progress matters, not which meeting holds which room, |
| 7 | # so every interval becomes a +1 event at its start and a -1 event at its end. |
| 8 | events: list[tuple[int, int]] = [] |
| 9 | for start, end in intervals: |
| 10 | events.append((start, 1)) # Room checkout: +1 |
| 11 | events.append((end, -1)) # Room vacated: -1 |
| 12 | |
| 13 | # 2. Sort Events by Time, Ends Before Starts on a Tie: |
| 14 | # Meetings are half-open [start, end): at a shared time the -1 must be applied before the +1, so |
| 15 | # a meeting ending at that time and one starting at it never count as two rooms at once. |
| 16 | events.sort(key=lambda ev: (ev[0], ev[1])) |
| 17 | |
| 18 | active_rooms = 0 # active_rooms is how many meetings are in progress right after the current event |
| 19 | max_rooms = 0 # max_rooms is the highest value active_rooms has reached |
| 20 | |
| 21 | # 3. Sweep the Timeline and Track the Peak: |
| 22 | # Each event changes active_rooms by exactly one, so checking right after every event sees every |
| 23 | # value active_rooms ever takes, including its peak. |
| 24 | for time, delta in events: |
| 25 | active_rooms += delta |
| 26 | max_rooms = max(max_rooms, active_rooms) |
| 27 | |
| 28 | return max_rooms |
time are applied, active_rooms is the number of meetings in progress, and max_rooms is the highest value it has reached.Turn each interval into two events, +1 at its start and -1 at its end. Sort the events by time and add them up in order: the running total is how many intervals are active at that moment, and its highest value is the peak. In the blueprint active_rooms is that running total and max_rooms its peak. The trap is a tie: when one meeting ends at the same time another starts, the -1 must be applied first, which sorting by (time, delta) does because -1 < 1; otherwise the room is counted twice.
See it run
Sweep Line, step by step
active_rooms 0max_rooms 0
Each column is one time point. Each row of numbers is one meeting. A dash marks the time points the meeting covers. The last row fills in as the events are applied.
+1a start event, applied now-1an end event, applied nowactivethe value of active_rooms right after this event
Each meeting gives two events. Sorted by (time, delta): (0, 1), (2, 1), (6, -1), (6, 1), (8, -1), (9, -1).
Gotcha 1 of 3
- The trap
Gotcha 1: Sorting the events by time alone
Mistakeevents.sort(key=lambda ev: ev[0])Fixevents.sort(key=lambda ev: (ev[0], ev[1]))What happens: A meeting
[start, end)is over atend, so a room is free again at that time. When one meeting ends as another starts, the-1must come first. A key on the time alone keeps tied events in input order. A+1can then come before the-1, and one room is counted twice.Example: intervals = [[10, 20], [0, 10]]Mistake: returns 2Fix: returns 1
- Many range updates must be applied and then every position read, as in Corporate Flight Bookings (LC 1109), not just the busiest moment. See Difference Array
Deep Dive
Why only the running count matters
This card builds on the Merge Intervals card. That card joins overlapping intervals into one. This card does not join them. It counts how many intervals are open at each moment.
The question for Meeting Rooms II (LC 253) is the largest number of meetings in progress at one time. It does not matter which meeting uses which room. So each meeting becomes two events: (start, 1) and (end, -1).
After the events are sorted by time, active_rooms += delta gives the number of meetings in progress right after each event. Each event changes the count by exactly one, so checking max_rooms after every event sees every value that active_rooms ever takes.
Example: in LC 253, intervals = [[0, 30], [5, 10], [15, 20]]. The sorted events are (0, 1), (5, 1), (10, -1), (15, 1), (20, -1), (30, -1). The count goes 1, 2, 1, 2, 1, 0. The peak is 2, so the answer is 2.
One trace: the -1 comes first at a tie
The board replays intervals = [[0, 6], [2, 8], [6, 9]]. The first meeting ends at 6 and the third meeting starts at 6.
The events are sorted by (time, delta). At time 6 there are two events, (6, -1) and (6, 1). Since -1 < 1, the -1 comes first. active_rooms goes from 2 to 1, and then back to 2. These two steps are marked as the trap on the board. The peak is 2.
If the +1 came first, active_rooms would go from 2 to 3 and then back to 2. max_rooms would be 3, which is wrong. The first meeting has already ended at 6, so it does not use a room at that time.
The meetings are half-open: [start, end). A meeting uses its room from start up to, but not including, end. This is why the end event is applied first.
Reading the answer, and the other tie rule
The answer is max_rooms, not active_rooms. After the last event, every meeting has ended, so active_rooms is 0.
The tie rule depends on the problem. In Meeting Rooms II (LC 253), a meeting ending at 10 and one starting at 10 can share a room, so the end comes first. For [[0, 10], [10, 20]] the answer is 1. With the +1 first, the loop returns 2.
Some problems count both end points as busy. Then a meeting that ends at 10 still uses its room at 10, and the start must come first. Read the statement of each problem and choose the order of delta in the sort key.
Python compares tuples left to right, so (ev[0], ev[1]) sorts by time first and by delta second. Sorting by ev[0] alone keeps tied events in input order, which can put a +1 before a -1.
The cost, and which problems fit
There are 2N events. Sorting them takes O(N log N) time, and the loop takes O(N) time. The events use O(N) space.
Example: for 1000 meetings, events holds 2000 entries. The loop runs 2000 times.
Any question about the most intervals open at once uses this card. Car Pooling (LC 1094) uses a delta equal to the number of passengers, and the limit is the capacity. Divide Intervals Into Minimum Number of Groups (LC 2406) is the same peak count.
If the coordinates are small, a difference array can replace the sort. Corporate Flight Bookings (LC 1109) adds to diff[first] and subtracts after diff[last], then takes the running sum.
If the question only needs to know whether two intervals overlap, as in Meeting Rooms (LC 252), sorting by start is enough. Use the Merge Intervals card for joined ranges.
60-Second Pitch
"Only the number of meetings in progress matters, so I turn each meeting into two events: +1 at its start and -1 at its end. I sort the events by time. At the same time, I apply the -1 first, because a meeting that ends at that time has already left its room. Then I add up the deltas in order and keep the highest value in max_rooms. That is O(N log N) time for the sort and O(N) space for the 2N events. I check the tie case, a single meeting, and that I return the peak and not the final count."
Common Questions
- Why split each interval into two events?
The answer depends only on how many intervals are open at each moment. A
+1event at the start and a-1event at the end give that count with one pass over sorted events. The pass does not need to know which interval is which.- Why must the -1 come before the +1 at the same time?
In Meeting Rooms II (LC 253), a meeting ends at its end time and frees its room. A meeting that starts at that time can use the same room. If the
+1is applied first, the count goes one too high for a moment, andmax_roomscan record a room that is not needed.- What are the time and space complexity?
O(N log N)time, because 2N events are sorted. The loop isO(N). The space isO(N)forevents.- How is this different from merging intervals?
Merging joins overlapping intervals into one range and loses how many were open. Sweep line keeps the count. Use merging when the question is about covered time or gaps. Use sweep line when the question is about the peak number of overlaps.
- Can you avoid the sort?
Yes, if the time values are small integers. Make an array
diff, add 1 at each start, subtract 1 after each end, and take the running sum. This is O(N + T), whereTis the range of time values, and it is how Corporate Flight Bookings (LC 1109) is solved.
SWE Reasoning
Spot it
- The question asks for the largest number of intervals that overlap at one moment.
- The wording is about capacity: rooms, cars, servers, passengers or bookings at the same time.
- Which interval holds which resource does not matter. Only the count matters.
- Merging the intervals would lose the count, because it joins them into one range.
- The ends are either half-open or closed, and the statement decides whether a shared time point counts twice.
The invariant, step by step
- Create
events. For eachstart, end, append(start, 1)and(end, -1). - Sort
eventsby(time, delta). At the same time, the-1events come before the+1events. - Set
active_rooms = 0andmax_rooms = 0. - For each
time, deltain the sorted events, doactive_rooms += delta. - Then set
max_rooms = max(max_rooms, active_rooms). Each event changes the count by one, so every value is checked. - After all events,
active_roomsis the number of meetings in progress right after the event, andmax_roomsis the highest value reached. Returnmax_rooms.
Traps
- Sorting by time alone. Tied events stay in input order, so a
+1can come before a-1. For[[10, 20], [0, 10]]the loop returns 2 instead of 1. - Putting
+1first on a tie. The key(ev[0], -ev[1])counts a meeting that ends at 10 and one that starts at 10 as two rooms. For[[0, 10], [10, 20]]it returns 2 instead of 1. - Returning
active_rooms. After the last event it is 0. Returnmax_rooms. - Tracking which meeting holds which room. The question asks only for a count. A list of rooms adds work and gives no extra information.
- Using the wrong tie rule. A problem with closed intervals needs the start first. Read whether the end time is still busy.
Practice
Builds on Merge Intervals, which you haven’t finished yet.
Sweep Line (Line Sweep)
You're given an array of meeting time intervals intervals, each entry [start_i, end_i]. Figure out the smallest number of conference rooms that lets every meeting happen -- no room may host two meetings whose times actually overlap (a meeting ending at end_i and another starting at start_j don't overlap once end_i <= start_j).
[0,30] overlaps both other meetings, but those two do not overlap each other, so 2 rooms are enough.Sweep Line
events (start: +1, end: -1) ➔ sort time with end before start ➔ max active count| 1 | def min_meeting_rooms(intervals: list[list[int]]) -> int: |
| 2 | """Sweep-Line Event Processing: minimum conference rooms required. |
| 3 | Time: O(N log N) | Space: O(N) |
| 4 | """ |
| 5 | # 1. Turn Each Interval into Two Point Events: |
| 6 | # Only the running count of meetings in progress matters, not which meeting holds which room, |
| 7 | # so every interval becomes a +1 event at its start and a -1 event at its end. |
| 8 | events: list[tuple[int, int]] = [] |
| 9 | for start, end in intervals: |
| 10 | events.append((start, 1)) # Room checkout: +1 |
| 11 | events.append((end, -1)) # Room vacated: -1 |
| 12 | |
| 13 | # 2. Sort Events by Time, Ends Before Starts on a Tie: |
| 14 | # Meetings are half-open [start, end): at a shared time the -1 must be applied before the +1, so |
| 15 | # a meeting ending at that time and one starting at it never count as two rooms at once. |
| 16 | events.sort(key=lambda ev: (ev[0], ev[1])) |
| 17 | |
| 18 | active_rooms = 0 # active_rooms is how many meetings are in progress right after the current event |
| 19 | max_rooms = 0 # max_rooms is the highest value active_rooms has reached |
| 20 | |
| 21 | # 3. Sweep the Timeline and Track the Peak: |
| 22 | # Each event changes active_rooms by exactly one, so checking right after every event sees every |
| 23 | # value active_rooms ever takes, including its peak. |
| 24 | for time, delta in events: |
| 25 | active_rooms += delta |
| 26 | max_rooms = max(max_rooms, active_rooms) |
| 27 | |
| 28 | return max_rooms |
time are applied, active_rooms is the number of meetings in progress, and max_rooms is the highest value it has reached.Turn each interval into two events, +1 at its start and -1 at its end. Sort the events by time and add them up in order: the running total is how many intervals are active at that moment, and its highest value is the peak. In the blueprint active_rooms is that running total and max_rooms its peak. The trap is a tie: when one meeting ends at the same time another starts, the -1 must be applied first, which sorting by (time, delta) does because -1 < 1; otherwise the room is counted twice.
See it run
Sweep Line, step by step
active_rooms 0max_rooms 0
Each column is one time point. Each row of numbers is one meeting. A dash marks the time points the meeting covers. The last row fills in as the events are applied.
+1a start event, applied now-1an end event, applied nowactivethe value of active_rooms right after this event
Each meeting gives two events. Sorted by (time, delta): (0, 1), (2, 1), (6, -1), (6, 1), (8, -1), (9, -1).
Gotcha 1 of 3
- The trap
Gotcha 1: Sorting the events by time alone
Mistakeevents.sort(key=lambda ev: ev[0])Fixevents.sort(key=lambda ev: (ev[0], ev[1]))What happens: A meeting
[start, end)is over atend, so a room is free again at that time. When one meeting ends as another starts, the-1must come first. A key on the time alone keeps tied events in input order. A+1can then come before the-1, and one room is counted twice.Example: intervals = [[10, 20], [0, 10]]Mistake: returns 2Fix: returns 1
- Many range updates must be applied and then every position read, as in Corporate Flight Bookings (LC 1109), not just the busiest moment. See Difference Array
Deep Dive
Why only the running count matters
This card builds on the Merge Intervals card. That card joins overlapping intervals into one. This card does not join them. It counts how many intervals are open at each moment.
The question for Meeting Rooms II (LC 253) is the largest number of meetings in progress at one time. It does not matter which meeting uses which room. So each meeting becomes two events: (start, 1) and (end, -1).
After the events are sorted by time, active_rooms += delta gives the number of meetings in progress right after each event. Each event changes the count by exactly one, so checking max_rooms after every event sees every value that active_rooms ever takes.
Example: in LC 253, intervals = [[0, 30], [5, 10], [15, 20]]. The sorted events are (0, 1), (5, 1), (10, -1), (15, 1), (20, -1), (30, -1). The count goes 1, 2, 1, 2, 1, 0. The peak is 2, so the answer is 2.
One trace: the -1 comes first at a tie
The board replays intervals = [[0, 6], [2, 8], [6, 9]]. The first meeting ends at 6 and the third meeting starts at 6.
The events are sorted by (time, delta). At time 6 there are two events, (6, -1) and (6, 1). Since -1 < 1, the -1 comes first. active_rooms goes from 2 to 1, and then back to 2. These two steps are marked as the trap on the board. The peak is 2.
If the +1 came first, active_rooms would go from 2 to 3 and then back to 2. max_rooms would be 3, which is wrong. The first meeting has already ended at 6, so it does not use a room at that time.
The meetings are half-open: [start, end). A meeting uses its room from start up to, but not including, end. This is why the end event is applied first.
Reading the answer, and the other tie rule
The answer is max_rooms, not active_rooms. After the last event, every meeting has ended, so active_rooms is 0.
The tie rule depends on the problem. In Meeting Rooms II (LC 253), a meeting ending at 10 and one starting at 10 can share a room, so the end comes first. For [[0, 10], [10, 20]] the answer is 1. With the +1 first, the loop returns 2.
Some problems count both end points as busy. Then a meeting that ends at 10 still uses its room at 10, and the start must come first. Read the statement of each problem and choose the order of delta in the sort key.
Python compares tuples left to right, so (ev[0], ev[1]) sorts by time first and by delta second. Sorting by ev[0] alone keeps tied events in input order, which can put a +1 before a -1.
The cost, and which problems fit
There are 2N events. Sorting them takes O(N log N) time, and the loop takes O(N) time. The events use O(N) space.
Example: for 1000 meetings, events holds 2000 entries. The loop runs 2000 times.
Any question about the most intervals open at once uses this card. Car Pooling (LC 1094) uses a delta equal to the number of passengers, and the limit is the capacity. Divide Intervals Into Minimum Number of Groups (LC 2406) is the same peak count.
If the coordinates are small, a difference array can replace the sort. Corporate Flight Bookings (LC 1109) adds to diff[first] and subtracts after diff[last], then takes the running sum.
If the question only needs to know whether two intervals overlap, as in Meeting Rooms (LC 252), sorting by start is enough. Use the Merge Intervals card for joined ranges.
60-Second Pitch
"Only the number of meetings in progress matters, so I turn each meeting into two events: +1 at its start and -1 at its end. I sort the events by time. At the same time, I apply the -1 first, because a meeting that ends at that time has already left its room. Then I add up the deltas in order and keep the highest value in max_rooms. That is O(N log N) time for the sort and O(N) space for the 2N events. I check the tie case, a single meeting, and that I return the peak and not the final count."
Common Questions
- Why split each interval into two events?
The answer depends only on how many intervals are open at each moment. A
+1event at the start and a-1event at the end give that count with one pass over sorted events. The pass does not need to know which interval is which.- Why must the -1 come before the +1 at the same time?
In Meeting Rooms II (LC 253), a meeting ends at its end time and frees its room. A meeting that starts at that time can use the same room. If the
+1is applied first, the count goes one too high for a moment, andmax_roomscan record a room that is not needed.- What are the time and space complexity?
O(N log N)time, because 2N events are sorted. The loop isO(N). The space isO(N)forevents.- How is this different from merging intervals?
Merging joins overlapping intervals into one range and loses how many were open. Sweep line keeps the count. Use merging when the question is about covered time or gaps. Use sweep line when the question is about the peak number of overlaps.
- Can you avoid the sort?
Yes, if the time values are small integers. Make an array
diff, add 1 at each start, subtract 1 after each end, and take the running sum. This is O(N + T), whereTis the range of time values, and it is how Corporate Flight Bookings (LC 1109) is solved.
SWE Reasoning
Spot it
- The question asks for the largest number of intervals that overlap at one moment.
- The wording is about capacity: rooms, cars, servers, passengers or bookings at the same time.
- Which interval holds which resource does not matter. Only the count matters.
- Merging the intervals would lose the count, because it joins them into one range.
- The ends are either half-open or closed, and the statement decides whether a shared time point counts twice.
The invariant, step by step
- Create
events. For eachstart, end, append(start, 1)and(end, -1). - Sort
eventsby(time, delta). At the same time, the-1events come before the+1events. - Set
active_rooms = 0andmax_rooms = 0. - For each
time, deltain the sorted events, doactive_rooms += delta. - Then set
max_rooms = max(max_rooms, active_rooms). Each event changes the count by one, so every value is checked. - After all events,
active_roomsis the number of meetings in progress right after the event, andmax_roomsis the highest value reached. Returnmax_rooms.
Traps
- Sorting by time alone. Tied events stay in input order, so a
+1can come before a-1. For[[10, 20], [0, 10]]the loop returns 2 instead of 1. - Putting
+1first on a tie. The key(ev[0], -ev[1])counts a meeting that ends at 10 and one that starts at 10 as two rooms. For[[0, 10], [10, 20]]it returns 2 instead of 1. - Returning
active_rooms. After the last event it is 0. Returnmax_rooms. - Tracking which meeting holds which room. The question asks only for a count. A list of rooms adds work and gives no extra information.
- Using the wrong tie rule. A problem with closed intervals needs the start first. Read whether the end time is still busy.