253. Meeting Rooms II 🔒
Problem Description
You are given an array of meeting time intervals where each interval is represented as intervals[i] = [start_i, end_i]
. Each interval indicates when a meeting starts and ends.
Your task is to find the minimum number of conference rooms required to schedule all the meetings without any conflicts. In other words, if two or more meetings overlap in time, they need separate conference rooms.
For example, if you have meetings [[0,30],[5,10],[15,20]]
, the first meeting runs from time 0 to 30. The second meeting (5 to 10) overlaps with the first, so you need a second room. The third meeting (15 to 20) also overlaps with the first, but since the second meeting has already ended by time 15, you can reuse that room. Therefore, you need a minimum of 2 conference rooms.
The solution uses a difference array technique. It tracks the changes in the number of ongoing meetings at each time point. By marking +1
when a meeting starts and -1
when it ends, then calculating the running sum, you can determine the maximum number of simultaneous meetings at any point in time, which equals the minimum number of rooms needed.
Intuition
To understand how many conference rooms we need, think about what happens at each moment in time. At any given moment, the number of rooms needed equals the number of meetings happening simultaneously.
Imagine a timeline where meetings start and end. When a meeting starts, we need one more room. When a meeting ends, we free up one room. The key insight is that we only care about these "change points" - the moments when meetings start or end.
We can track these changes using a difference array. For each meeting with interval [start, end]
, we mark +1
at the start time (one more room needed) and -1
at the end time (one room freed). This creates a record of all the changes in room demand throughout the day.
By calculating the running sum (prefix sum) of this difference array, we get the actual number of rooms in use at each time point. The running sum works because:
- Starting from 0 rooms
- Each
+1
we encounter means a meeting started (add a room) - Each
-1
we encounter means a meeting ended (free a room) - The sum at any point tells us exactly how many meetings are happening simultaneously
The maximum value in this running sum represents the peak moment when the most meetings overlap - this is our answer, the minimum number of conference rooms required.
For example, with meetings [[0,30],[5,10],[15,20]]
:
- At time 0:
+1
(1 room in use) - At time 5:
+1
(2 rooms in use) - At time 10:
-1
(1 room in use) - At time 15:
+1
(2 rooms in use) - At time 20:
-1
(1 room in use) - At time 30:
-1
(0 rooms in use)
The maximum is 2, so we need 2 conference rooms.
Solution Approach
The implementation uses a difference array to efficiently track room usage changes over time.
Step 1: Find the maximum end time
m = max(e[1] for e in intervals)
We first determine the maximum end time among all meetings to know the size of our difference array. This ensures our array covers the entire time range.
Step 2: Create and populate the difference array
d = [0] * (m + 1) for l, r in intervals: d[l] += 1 d[r] -= 1
We create a difference array d
of size m + 1
initialized with zeros. For each meeting interval [l, r]
:
- Increment
d[l]
by 1 to mark a meeting starting at timel
- Decrement
d[r]
by 1 to mark a meeting ending at timer
Note that multiple meetings can start or end at the same time, so we use +=
and -=
to accumulate the changes.
Step 3: Calculate the prefix sum and find the maximum
ans = s = 0
for v in d:
s += v
ans = max(ans, s)
return ans
We iterate through the difference array, maintaining a running sum s
. At each time point:
- Add the current value
v
to the running sums
- The running sum represents the number of rooms in use at that moment
- Track the maximum value seen in
ans
The maximum value of the running sum represents the peak number of simultaneous meetings, which equals the minimum number of conference rooms required.
Time Complexity: O(n + m)
where n
is the number of intervals and m
is the maximum end time
Space Complexity: O(m)
for the difference array
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with meetings [[0,4],[2,6],[3,5],[7,9]]
.
Step 1: Find maximum end time The end times are 4, 6, 5, and 9. Maximum is 9, so we'll create an array of size 10.
Step 2: Build the difference array
Initialize: d = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
For each meeting, mark start with +1 and end with -1:
-
Meeting [0,4]: Mark +1 at index 0, -1 at index 4
d = [1, 0, 0, 0, -1, 0, 0, 0, 0, 0]
-
Meeting [2,6]: Mark +1 at index 2, -1 at index 6
d = [1, 0, 1, 0, -1, 0, -1, 0, 0, 0]
-
Meeting [3,5]: Mark +1 at index 3, -1 at index 5
d = [1, 0, 1, 1, -1, -1, -1, 0, 0, 0]
-
Meeting [7,9]: Mark +1 at index 7, -1 at index 9
d = [1, 0, 1, 1, -1, -1, -1, 1, 0, -1]
Step 3: Calculate running sum to find maximum rooms needed
Time | Change (d[i]) | Running Sum | Rooms in Use |
---|---|---|---|
0 | +1 | 1 | 1 room |
1 | 0 | 1 | 1 room |
2 | +1 | 2 | 2 rooms |
3 | +1 | 3 | 3 rooms ← maximum |
4 | -1 | 2 | 2 rooms |
5 | -1 | 1 | 1 room |
6 | -1 | 0 | 0 rooms |
7 | +1 | 1 | 1 room |
8 | 0 | 1 | 1 room |
9 | -1 | 0 | 0 rooms |
The maximum running sum is 3, which occurs at time 3 when three meetings ([0,4], [2,6], and [3,5]) are all happening simultaneously. Therefore, we need a minimum of 3 conference rooms.
Solution Implementation
1class Solution:
2 def minMeetingRooms(self, intervals: List[List[int]]) -> int:
3 # Find the maximum end time among all intervals
4 max_end_time = max(interval[1] for interval in intervals)
5
6 # Create a difference array to track room changes at each time point
7 # difference[i] represents the change in number of rooms needed at time i
8 difference = [0] * (max_end_time + 1)
9
10 # Mark the start and end of each meeting
11 for start_time, end_time in intervals:
12 difference[start_time] += 1 # One more room needed at start
13 difference[end_time] -= 1 # One room freed at end
14
15 # Calculate the maximum number of concurrent meetings
16 max_rooms = 0
17 current_rooms = 0
18
19 # Sweep through time and track the running sum of rooms needed
20 for room_change in difference:
21 current_rooms += room_change
22 max_rooms = max(max_rooms, current_rooms)
23
24 return max_rooms
25
1class Solution {
2 public int minMeetingRooms(int[][] intervals) {
3 // Find the maximum end time among all intervals
4 int maxEndTime = 0;
5 for (int[] interval : intervals) {
6 maxEndTime = Math.max(maxEndTime, interval[1]);
7 }
8
9 // Create a difference array to track room usage changes at each time point
10 // Index represents time, value represents change in number of rooms needed
11 int[] differenceArray = new int[maxEndTime + 1];
12
13 // Mark the start and end of each meeting in the difference array
14 // +1 at start time means one more room is needed
15 // -1 at end time means one room is freed
16 for (int[] interval : intervals) {
17 differenceArray[interval[0]]++;
18 differenceArray[interval[1]]--;
19 }
20
21 // Calculate the maximum number of rooms needed at any point in time
22 int maxRooms = 0;
23 int currentRooms = 0;
24
25 // Iterate through time and accumulate the room count
26 for (int roomChange : differenceArray) {
27 currentRooms += roomChange; // Update current rooms in use
28 maxRooms = Math.max(maxRooms, currentRooms); // Track maximum rooms needed
29 }
30
31 return maxRooms;
32 }
33}
34
1class Solution {
2public:
3 int minMeetingRooms(vector<vector<int>>& intervals) {
4 // Find the maximum end time among all intervals
5 int maxEndTime = 0;
6 for (const auto& interval : intervals) {
7 maxEndTime = max(maxEndTime, interval[1]);
8 }
9
10 // Create a difference array to track meeting room changes at each time point
11 // differenceArray[i] represents the change in number of meetings at time i
12 vector<int> differenceArray(maxEndTime + 1, 0);
13
14 // Mark the start and end of each meeting in the difference array
15 // +1 at start time means a meeting starts (need one more room)
16 // -1 at end time means a meeting ends (free up one room)
17 for (const auto& interval : intervals) {
18 differenceArray[interval[0]]++;
19 differenceArray[interval[1]]--;
20 }
21
22 // Calculate the maximum number of concurrent meetings
23 int maxRooms = 0;
24 int currentRooms = 0;
25
26 // Traverse through time and accumulate the number of active meetings
27 for (int change : differenceArray) {
28 currentRooms += change;
29 maxRooms = max(maxRooms, currentRooms);
30 }
31
32 return maxRooms;
33 }
34};
35
1/**
2 * Finds the minimum number of meeting rooms required to accommodate all meetings
3 * @param intervals - Array of meeting intervals where each interval is [start, end]
4 * @returns The minimum number of meeting rooms needed
5 */
6function minMeetingRooms(intervals: number[][]): number {
7 // Find the maximum end time among all intervals
8 const maxEndTime: number = Math.max(...intervals.map(([start, end]) => end));
9
10 // Create a difference array to track meeting room changes at each time point
11 // Index represents time, value represents change in room count
12 const differenceArray: number[] = Array(maxEndTime + 1).fill(0);
13
14 // Mark the start and end of each meeting in the difference array
15 // Increment at start time (room needed), decrement at end time (room freed)
16 for (const [startTime, endTime] of intervals) {
17 differenceArray[startTime]++;
18 differenceArray[endTime]--;
19 }
20
21 // Track maximum rooms needed and current room count
22 let maxRoomsNeeded: number = 0;
23 let currentRoomCount: number = 0;
24
25 // Iterate through each time point to find the maximum concurrent meetings
26 for (const roomChange of differenceArray) {
27 currentRoomCount += roomChange;
28 maxRoomsNeeded = Math.max(maxRoomsNeeded, currentRoomCount);
29 }
30
31 return maxRoomsNeeded;
32}
33
Time and Space Complexity
The time complexity is O(n + m)
where n
is the number of intervals (meetings) and m
is the maximum end time among all intervals.
- Finding the maximum end time takes
O(n)
time as it iterates through all intervals once - Creating the difference array
d
takesO(m)
space initialization - Updating the difference array with start and end points takes
O(n)
time (two operations per interval) - The final sweep through the difference array to calculate the maximum overlapping meetings takes
O(m)
time - Overall:
O(n) + O(n) + O(m) = O(n + m)
The space complexity is O(m)
where m
is the maximum end time among all intervals.
- The difference array
d
has sizem + 1
, requiringO(m)
space - Other variables (
ans
,s
, loop variables) useO(1)
space - Overall:
O(m)
Common Pitfalls
1. Memory Inefficiency with Large Time Values
The current solution creates an array of size max_end_time + 1
, which can be extremely memory-inefficient when dealing with large time values. For example, if you have meetings like [[1000000, 1000005], [1000002, 1000007]]
, the solution would create an array of over 1 million elements just to track a few meetings.
Solution: Use a sorted events approach instead of a difference array:
class Solution:
def minMeetingRooms(self, intervals: List[List[int]]) -> int:
# Create events for all start and end times
events = []
for start, end in intervals:
events.append((start, 1)) # 1 for start
events.append((end, -1)) # -1 for end
# Sort events by time, with ends before starts at the same time
events.sort(key=lambda x: (x[0], x[1]))
max_rooms = 0
current_rooms = 0
for time, change in events:
current_rooms += change
max_rooms = max(max_rooms, current_rooms)
return max_rooms
2. Handling Same Time Start and End
When a meeting ends at the exact same time another begins (e.g., [0, 10]
and [10, 20]
), the original difference array approach handles this correctly by processing both the -1
and +1
at time 10. However, if you modify the solution to use a dictionary or other data structure, you might accidentally overwrite values instead of accumulating them.
Incorrect approach:
# Wrong - overwrites instead of accumulates difference = {} for start, end in intervals: difference[start] = 1 # Should be += difference[end] = -1 # Should be -=
Correct approach:
from collections import defaultdict
difference = defaultdict(int)
for start, end in intervals:
difference[start] += 1
difference[end] -= 1
3. Integer Overflow in Other Languages
While Python handles arbitrarily large integers, implementing this solution in languages like Java or C++ with fixed-size integers could cause overflow if the time values are near the maximum integer limit. Creating an array of size Integer.MAX_VALUE + 1
would cause an error.
Solution: Always validate input ranges or use the sorted events approach which doesn't depend on the absolute values of the times, only their relative ordering.
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Want a Structured Path to Master System Design Too? Don’t Miss This!