Meeting Rooms III
Difficulty: Hard Source: NeetCode
Problem
You are given an integer
nrepresenting the number of rooms numbered0ton - 1. You are also given a 2D integer arraymeetingswheremeetings[i] = [start_i, end_i]represents the start and end times of meetingi(end time is exclusive — the room is free again atend_i).All meetings must be held. Each meeting is assigned to the lowest-numbered available room. If no room is available, the meeting is delayed until the earliest room becomes free. The delayed meeting retains its original duration. Return the room number that held the most meetings. If multiple rooms held the same number, return the room with the lowest number.
Example 1: Input:
n = 2,meetings = [[0,10],[1,5],[2,7],[3,4]]Output:0Example 2: Input:
n = 3,meetings = [[1,20],[2,10],[3,5],[4,9],[6,8]]Output:1Constraints:
1 <= n <= 1001 <= meetings.length <= 10^50 <= start_i < end_i <= 5 * 10^5- All values of
start_iare unique.
Prerequisites
Before attempting this problem, you should be comfortable with:
- Min-heap (priority queue) — quickly finding the room that becomes free soonest and the lowest-numbered available room
- Sorting — processing meetings in chronological order
- Simulation — carefully tracking the state of each room at each step
1. Brute Force
Intuition
Sort meetings by start time and process them one by one. For each meeting, scan all rooms in order to find the lowest-numbered free one. If none is free, find which room ends soonest, delay the meeting to that time, and use that room. Simple but slow for large inputs.
Algorithm
- Sort
meetingsby start time. - Maintain
end_times[i]= when roominext becomes free (initially 0). - Maintain
count[i]= meetings held in roomi. - For each meeting
[start, end]:- Find the lowest-indexed room
rwhereend_times[r] <= start. If found, assign this meeting. - Otherwise, find the room
rwith the smallestend_times[r], delay the meeting: new end =end_times[r] + (end - start). - Update
end_times[r]andcount[r].
- Find the lowest-indexed room
- Return the room with the highest count (lowest index on ties).
Solution
def most_booked_brute(n, meetings):
meetings.sort()
end_times = [0] * n
count = [0] * n
for start, end in meetings:
duration = end - start
# Try to find the lowest-indexed free room
assigned = -1
for i in range(n):
if end_times[i] <= start:
assigned = i
break
if assigned == -1:
# No room available — use the one that frees up soonest
earliest_end = min(end_times)
assigned = end_times.index(earliest_end)
end_times[assigned] = earliest_end + duration
else:
end_times[assigned] = end
count[assigned] += 1
# Return room with most meetings (lowest index on tie)
return count.index(max(count))
print(most_booked_brute(2, [[0,10],[1,5],[2,7],[3,4]])) # 0
print(most_booked_brute(3, [[1,20],[2,10],[3,5],[4,9],[6,8]])) # 1
Complexity
- Time:
O(m * n)— for each meeting, scan all n rooms - Space:
O(n)
2. Two Min-Heaps
Intuition
Use two heaps to track room state efficiently. One min-heap holds available room numbers (so the smallest is always at the top). Another min-heap holds occupied rooms as (end_time, room_number) pairs (so the earliest-freeing room is always at the top).
For each meeting in sorted order: first, pop all rooms from the occupied heap that have already ended and move them back to the available heap. Then, if the available heap is non-empty, grab the lowest-numbered room. If not, pop the earliest-ending room from the occupied heap, delay the meeting to start when that room is free, and reuse it. Push the room onto the occupied heap with its new end time.
Algorithm
- Sort
meetingsby start time. - Initialize
available = min-heap([0, 1, ..., n-1])andoccupied = min-heap()(pairs of(end_time, room_id)). - Initialize
count = [0] * n. - For each meeting
[start, end]:- Move rooms from
occupiedtoavailableif their end time ≤start. - If
availableis non-empty: pop the lowest-numbered room, schedule meeting ending atend. - Else: pop
(earliest_end, room)fromoccupied, schedule ending atearliest_end + (end - start). - Push
(new_end, room)ontooccupied, incrementcount[room].
- Move rooms from
- Return the room with the highest count (ties broken by lowest index).
Solution
import heapq
def most_booked(n, meetings):
meetings.sort()
available = list(range(n)) # min-heap of free room numbers
heapq.heapify(available)
occupied = [] # min-heap of (end_time, room_id)
count = [0] * n
for start, end in meetings:
# Free rooms whose meetings have ended
while occupied and occupied[0][0] <= start:
end_time, room = heapq.heappop(occupied)
heapq.heappush(available, room)
if available:
# Assign to lowest-numbered free room
room = heapq.heappop(available)
heapq.heappush(occupied, (end, room))
else:
# All rooms busy — wait for the earliest-ending one
earliest_end, room = heapq.heappop(occupied)
new_end = earliest_end + (end - start)
heapq.heappush(occupied, (new_end, room))
count[room] += 1
# Room with most meetings; lowest index wins on tie
max_count = max(count)
return count.index(max_count)
print(most_booked(2, [[0,10],[1,5],[2,7],[3,4]])) # 0
print(most_booked(3, [[1,20],[2,10],[3,5],[4,9],[6,8]])) # 1
print(most_booked(2, [[0,10],[1,5],[2,7],[3,4]])) # 0
Complexity
- Time:
O(m log m + m log n)— sorting meetings + heap operations per meeting - Space:
O(n + m)— two heaps
Common Pitfalls
Freeing rooms too eagerly. Only free a room when end_time <= start (the room is free by the time the meeting begins). Using strict < would incorrectly prevent a room from being reused when back-to-back scheduling is possible.
Forgetting to preserve original duration when delaying. A delayed meeting keeps its original length. If a meeting [3,7] (duration 4) is delayed until room frees at time 10, the new end is 10 + 4 = 14, not 10 + 7 = 17.
Not breaking ties correctly. When multiple rooms free up at the same time, the problem requires the lowest-numbered room. The tuple comparison (end_time, room_id) in the occupied heap handles this naturally — Python compares tuples lexicographically.