Meeting Rooms II
Difficulty: Medium Source: NeetCode
Problem
Given an array of meeting time intervals
intervalswhereintervals[i] = [start_i, end_i], return the minimum number of conference rooms required.Example 1: Input:
intervals = [[0,30],[5,10],[15,20]]Output:2Example 2: Input:
intervals = [[7,10],[2,4]]Output:1Constraints:
1 <= intervals.length <= 10^40 <= start_i < end_i <= 10^6
Prerequisites
Before attempting this problem, you should be comfortable with:
- Sorting — sorting meetings by start time to process them in order
- Min-heap (priority queue) — quickly finding the room that frees up soonest
- Two pointers — the alternative approach using sorted start and end arrays
1. Brute Force
Intuition
Simulate the process directly: for each meeting, scan all existing rooms to find one that has already finished. If none is free, open a new room. This is correct but slow because the “find a free room” scan is linear.
Algorithm
- Sort
intervalsby start time. - Maintain a list
roomswhere each entry is the end time of the meeting currently occupying that room. - For each meeting
[start, end]:- Find any room whose current meeting ends by
start. If found, update that room’s end time toend. - Otherwise add a new room with end time
end.
- Find any room whose current meeting ends by
- Return
len(rooms).
Solution
def min_meeting_rooms_brute(intervals):
intervals.sort(key=lambda x: x[0])
rooms = [] # each entry = end time of meeting in that room
for start, end in intervals:
# Try to find a free room
assigned = False
for i in range(len(rooms)):
if rooms[i] <= start:
rooms[i] = end
assigned = True
break
if not assigned:
rooms.append(end)
return len(rooms)
print(min_meeting_rooms_brute([[0,30],[5,10],[15,20]])) # 2
print(min_meeting_rooms_brute([[7,10],[2,4]])) # 1
print(min_meeting_rooms_brute([[1,5],[2,6],[3,7]])) # 3
Complexity
- Time:
O(n²)— for each meeting, we scan all rooms - Space:
O(n)— rooms list
2. Min-Heap of End Times
Intuition
Sort meetings by start time. Maintain a min-heap that holds the end times of all currently occupied rooms — so the top of the heap is the room that frees up soonest. When the next meeting starts, peek at the heap: if the earliest-ending room is already free (its end time ≤ current meeting’s start), pop it and reuse it; otherwise push a new room. The heap size at the end equals the number of rooms needed.
Algorithm
- Sort
intervalsby start time. - Initialize a min-heap
heap. - For each meeting
[start, end]:- If
heapis non-empty andheap[0] <= start, pop the earliest-ending room (it is free). - Push
endonto the heap (allocate or reuse a room).
- If
- Return
len(heap).
Solution
import heapq
def min_meeting_rooms(intervals):
intervals.sort(key=lambda x: x[0])
heap = [] # min-heap of end times
for start, end in intervals:
if heap and heap[0] <= start:
heapq.heappop(heap) # reuse the earliest-freed room
heapq.heappush(heap, end)
return len(heap)
print(min_meeting_rooms([[0,30],[5,10],[15,20]])) # 2
print(min_meeting_rooms([[7,10],[2,4]])) # 1
print(min_meeting_rooms([[1,5],[2,6],[3,7]])) # 3
print(min_meeting_rooms([[1,10]])) # 1
Complexity
- Time:
O(n log n)— sorting + heap operations (each push/pop is O(log n)) - Space:
O(n)— heap can hold all n meetings in the worst case
3. Two Pointers on Sorted Start and End Arrays
Intuition
Separate the start times and end times into two sorted arrays. Use two pointers s and e. Process events in chronological order: if the next event is a start, we need a new room (increment rooms); if the next event is an end, a room is freed (decrement rooms, advance e). Track the maximum rooms used at any point.
Algorithm
- Create
starts = sorted([i[0] for i in intervals])andends = sorted([i[1] for i in intervals]). - Initialize
rooms = 0,max_rooms = 0,s = 0,e = 0. - While
s < n:- If
starts[s] < ends[e]: a meeting starts before the earliest end — new room needed. Incrementrooms, advances. - Else: a meeting ends before or when this one starts — free a room. Decrement
rooms, advancee. - Update
max_rooms = max(max_rooms, rooms).
- If
- Return
max_rooms.
Solution
def min_meeting_rooms_two_pointers(intervals):
n = len(intervals)
starts = sorted(i[0] for i in intervals)
ends = sorted(i[1] for i in intervals)
rooms = 0
max_rooms = 0
e = 0
for s in range(n):
if starts[s] < ends[e]:
rooms += 1
else:
rooms -= 1
e += 1
max_rooms = max(max_rooms, rooms)
return max_rooms
print(min_meeting_rooms_two_pointers([[0,30],[5,10],[15,20]])) # 2
print(min_meeting_rooms_two_pointers([[7,10],[2,4]])) # 1
print(min_meeting_rooms_two_pointers([[1,5],[2,6],[3,7]])) # 3
Complexity
- Time:
O(n log n)— two sorts - Space:
O(n)— two extra arrays
Common Pitfalls
Using < vs <= when checking if a room is free. If a meeting ends at time 5 and another starts at time 5, the room IS free. Use heap[0] <= start (not <) to allow back-to-back meetings in the same room.
Forgetting to sort before using the heap. The heap approach only works correctly when meetings are processed in start-time order. Sorting first is mandatory.
Confusing rooms opened vs maximum simultaneous rooms. The question asks for the peak concurrent rooms. The heap size at the end gives you this naturally — it counts rooms that were never freed.