Meeting Rooms III
HardPrompt
You are given an integer n. There are n rooms numbered from 0 to n - 1.
You are given a 2D integer array meetings where meetings[i] = [starti, endi] means that a meeting will be held during the half-closed time interval [starti, endi). All the values of starti are unique.
Meetings are allocated to rooms in the following manner:
- Each meeting will take place in the unused room with the lowest number.
- If there are no available rooms, the meeting will be delayed until a room becomes free. The delayed meeting should have the same duration as the original meeting.
- When a room becomes unused, meetings that have an earlier original start time should be given the room.
Return the number of the room that held the most meetings. If there are multiple rooms, return the room with the lowest number.
A half-closed interval [a, b) is the interval between a and b including a and not including b.
Example 1:
Input: n = 2, meetings = [[0,10],[1,5],[2,7],[3,4]]
Output: 0
Explanation:
- At time 0, both rooms are not being used. The first meeting starts in room 0.
- At time 1, only room 1 is not being used. The second meeting starts in room 1.
- At time 2, both rooms are being used. The third meeting is delayed.
- At time 3, both rooms are being used. The fourth meeting is delayed.
- At time 5, the meeting in room 1 finishes. The third meeting starts in room 1 for the time period [5,10).
- At time 10, the meetings in both rooms finish. The fourth meeting starts in room 0 for the time period [10,11).
Both rooms 0 and 1 held 2 meetings, so we return 0. Example 2:
Input: n = 3, meetings = [[1,20],[2,10],[3,5],[4,9],[6,8]]
Output: 1
Explanation:
- At time 1, all three rooms are not being used. The first meeting starts in room 0.
- At time 2, rooms 1 and 2 are not being used. The second meeting starts in room 1.
- At time 3, only room 2 is not being used. The third meeting starts in room 2.
- At time 4, all three rooms are being used. The fourth meeting is delayed.
- At time 5, the meeting in room 2 finishes. The fourth meeting starts in room 2 for the time period [5,10).
- At time 6, all three rooms are being used. The fifth meeting is delayed.
- At time 10, the meetings in rooms 1 and 2 finish. The fifth meeting starts in room 1 for the time period [10,12).
Room 0 held 1 meeting while rooms 1 and 2 each held 2 meetings, so we return 1.
Constraints:
1 <= n <= 1001 <= meetings.length <= 105meetings[i].length == 20 <= starti < endi <= 5 * 105- All the values of
startiare unique.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach simulates the meeting allocation process by iterating through meetings sorted by start time. For each meeting, it performs a linear scan through the rooms to find an available one or the one that frees up earliest. This is straightforward but less efficient for a large number of rooms.
Algorithm
- Sort the
meetingsarray by start time. - Initialize
roomAvailabilityTimearray of sizenwith zeros to track when each room becomes free. - Initialize
meetingCountsarray of sizenwith zeros. - Iterate through each sorted
meeting [start, end]:- Linearly scan rooms
0ton-1to find the first available roomi(whereroomAvailabilityTime[i] <= start). - If an available room
iis found, assign the meeting to it. UpdateroomAvailabilityTime[i] = endand incrementmeetingCounts[i]. - If no room is available, find the room
jthat will be free the earliest by finding the minimumroomAvailabilityTime. Assign the delayed meeting to roomj. The new end time will beroomAvailabilityTime[j] + (end - start). UpdateroomAvailabilityTime[j]and incrementmeetingCounts[j].
- Linearly scan rooms
- After iterating through all meetings, find the room index with the maximum count in
meetingCounts. Handle ties by choosing the smaller index.
Walkthrough
In this method, we directly simulate the process described in the problem. We maintain an array, roomAvailabilityTime, of size n, where each element stores the time when the corresponding room will become free. Another array, meetingCounts, of the same size, keeps track of how many meetings each room has hosted.
First, we must sort the meetings array by their start times. This ensures we process meetings in chronological order, which is a key requirement for correctly handling priorities for delayed meetings.
We then iterate through each sorted meeting [start, end]. For each meeting, we attempt to find an available room. We do this by scanning through our roomAvailabilityTime array from index 0 to n-1. The first room i for which roomAvailabilityTime[i] <= start is the one we select, as it satisfies both being available and having the lowest index.
If such a room is found, we schedule the meeting in it. This involves updating roomAvailabilityTime[i] to the meeting's end time and incrementing meetingCounts[i].
If, after checking all rooms, none are available at the meeting's start time, the meeting must be delayed. It has to wait for the first room to become free. To find this room, we perform another linear scan over roomAvailabilityTime to find the room j with the minimum availability time. This meeting will start at roomAvailabilityTime[j]. The duration of the meeting remains end - start. Therefore, the new end time for this meeting in room j will be roomAvailabilityTime[j] + (end - start). We update roomAvailabilityTime[j] to this new value and increment meetingCounts[j].
After processing all meetings, we iterate through the meetingCounts array to find the room index that has the highest count. In case of a tie, the problem specifies to return the room with the lower index, which we can handle during our search for the maximum.
import java.util.Arrays; class Solution { public int mostBooked(int n, int[][] meetings) { Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); long[] roomAvailabilityTime = new long[n]; int[] meetingCounts = new int[n]; for (int[] meeting : meetings) { int start = meeting[0]; int end = meeting[1]; boolean roomFound = false; int earliestFreeRoom = 0; long earliestFreeTime = Long.MAX_VALUE; // Find an available room with the lowest index for (int i = 0; i < n; i++) { if (roomAvailabilityTime[i] <= start) { roomAvailabilityTime[i] = end; meetingCounts[i]++; roomFound = true; break; } if (roomAvailabilityTime[i] < earliestFreeTime) { earliestFreeTime = roomAvailabilityTime[i]; earliestFreeRoom = i; } } // If no room was available, use the one that becomes free earliest if (!roomFound) { roomAvailabilityTime[earliestFreeRoom] += (end - start); meetingCounts[earliestFreeRoom]++; } } int maxMeetings = -1; int resultRoom = -1; for (int i = 0; i < n; i++) { if (meetingCounts[i] > maxMeetings) { maxMeetings = meetingCounts[i]; resultRoom = i; } } return resultRoom; }}Complexity
Time
O(M log M + M * N), where M is the number of meetings and N is the number of rooms. Sorting takes O(M log M). The main loop iterates M times, and inside it, we may perform a linear scan of N rooms, resulting in O(M * N).
Space
O(N) to store the availability time and meeting counts for each room.
Trade-offs
Pros
Simple logic, easy to understand and implement.
Low memory overhead, uses basic arrays.
Cons
Inefficient time complexity O(M*N), which can be slow if M and N are large.
This is a more efficient approach that uses two priority queues (min-heaps) to manage room allocation. One heap tracks available rooms to quickly find the one with the lowest index, and the other tracks occupied rooms to quickly find the one that will free up next. This avoids costly linear scans.
Algorithm
- Sort the
meetingsarray by start time. - Initialize a min-heap
availableRoomswith all room indices from0ton-1. - Initialize an empty min-heap
occupiedRoomsto store pairs(endTime, roomIndex), ordered byendTime. - Initialize a
meetingCountsarray of sizenwith zeros. - For each
meeting [start, end]in the sorted list:- Free up any rooms in
occupiedRoomsthat have anendTimeless than or equal to the current meeting'sstarttime. Move their indices toavailableRooms. - If
availableRoomsis not empty, poll the lowest-indexed room. Schedule the meeting there, update its count, and add(end, roomIndex)tooccupiedRooms. - If
availableRoomsis empty, the meeting is delayed. Poll the earliest-finishing room fromoccupiedRooms. Calculate the new end time based on the room's free time and meeting duration. Update its count and add the new(newEndTime, roomIndex)back tooccupiedRooms.
- Free up any rooms in
- After processing all meetings, find the room index with the maximum count, breaking ties by choosing the smaller index.
Walkthrough
This approach optimizes the simulation by using priority queues (min-heaps) to efficiently manage room states. We use two heaps:
availableRooms: A min-heap storing the indices of rooms that are currently free. Being a min-heap, it allows us to retrieve the available room with the lowest index inO(log N)time.occupiedRooms: A min-heap storing pairs of(endTime, roomIndex). It is ordered byendTime, enabling us to find out which occupied room will become free next inO(log N)time.
As with the naive approach, we start by sorting meetings by their start time. We initialize availableRooms with all room indices from 0 to n-1.
We then iterate through the sorted meetings. For each meeting [start, end]:
First, we advance our simulation time to start. We check the occupiedRooms heap for any rooms that should have become free by now (i.e., their endTime <= start). We poll all such rooms and add their indices back into the availableRooms heap.
Next, we need to schedule the current meeting. We check if availableRooms is empty.
- If it's not empty, we have at least one free room. We poll from
availableRoomsto get the one with the lowest index. We schedule the meeting in this room, which means we add a new entry(end, roomIndex)to theoccupiedRoomsheap and increment the room's meeting count. - If
availableRoomsis empty, all rooms are busy. The meeting is delayed. It must wait for the next available slot. This slot is determined by the room at the top of theoccupiedRoomsheap (the one with the earliestendTime). We poll this room(earliestFreeTime, roomIndex). The current meeting will start atearliestFreeTime. Its duration isend - start, so its new end time isearliestFreeTime + (end - start). We add(newEndTime, roomIndex)back tooccupiedRoomsand update the meeting count.
After processing all meetings, we find the room with the highest meeting count to return as the result.
import java.util.Arrays;import java.util.PriorityQueue; class Solution { public int mostBooked(int n, int[][] meetings) { Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); // Min-heap to store available room numbers PriorityQueue<Integer> availableRooms = new PriorityQueue<>(); for (int i = 0; i < n; i++) { availableRooms.add(i); } // Min-heap to store occupied rooms, sorted by end time. // Stores {endTime, roomNumber} PriorityQueue<long[]> occupiedRooms = new PriorityQueue<>((a, b) -> { if (a[0] != b[0]) { return Long.compare(a[0], b[0]); } return Long.compare(a[1], b[1]); // Tie-break by room number }); int[] meetingCounts = new int[n]; for (int[] meeting : meetings) { long start = meeting[0]; long end = meeting[1]; // Free up rooms that have finished their meetings by the current start time while (!occupiedRooms.isEmpty() && occupiedRooms.peek()[0] <= start) { long[] freedRoom = occupiedRooms.poll(); availableRooms.add((int) freedRoom[1]); } if (!availableRooms.isEmpty()) { // An available room exists, use the one with the smallest index int roomToUse = availableRooms.poll(); occupiedRooms.add(new long[]{(long)end, roomToUse}); meetingCounts[roomToUse]++; } else { // No rooms are available, delay the meeting // Get the room that will be free the earliest long[] nextFreeRoom = occupiedRooms.poll(); long earliestFreeTime = nextFreeRoom[0]; int roomToUse = (int) nextFreeRoom[1]; long duration = end - start; long newEndTime = earliestFreeTime + duration; occupiedRooms.add(new long[]{newEndTime, roomToUse}); meetingCounts[roomToUse]++; } } int maxMeetings = -1; int resultRoom = -1; for (int i = 0; i < n; i++) { if (meetingCounts[i] > maxMeetings) { maxMeetings = meetingCounts[i]; resultRoom = i; } } return resultRoom; }}Complexity
Time
O(M log M + M log N), where M is the number of meetings and N is the number of rooms. Sorting takes O(M log M). Each of the M meetings involves a constant number of heap operations, which take O(log N) time. Thus, the total time for processing all meetings is O(M log N).
Space
O(N) to store the elements in the two heaps and the meeting counts. In the worst case, all N rooms could be in one of the heaps.
Trade-offs
Pros
Highly efficient time complexity O(M log M + M log N), suitable for the given constraints.
Elegantly models the problem's state transitions using priority queues.
Cons
More complex to implement compared to the naive approach due to the use of heaps.
Slightly higher constant factor overhead from heap operations.
Solutions
Solution
class Solution {public int mostBooked(int n, int[][] meetings) { Arrays.sort(meetings, (a, b)->a[0] - b[0]); PriorityQueue<int[]> busy = new PriorityQueue<>((a, b)->a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]); PriorityQueue<Integer> idle = new PriorityQueue<>(); for (int i = 0; i < n; ++i) { idle.offer(i); } int[] cnt = new int[n]; for (var v : meetings) { int s = v[0], e = v[1]; while (!busy.isEmpty() && busy.peek()[0] <= s) { idle.offer(busy.poll()[1]); } int i = 0; if (!idle.isEmpty()) { i = idle.poll(); busy.offer(new int[]{e, i}); } else { var x = busy.poll(); i = x[1]; busy.offer(new int[]{x[0] + e - s, i}); } ++cnt[i]; } int ans = 0; for (int i = 0; i < n; ++i) { if (cnt[ans] < cnt[i]) { ans = i; } } return ans; }}using ll = long long ; using pii = pair < ll , int > ; class Solution { public: int mostBooked ( int n , vector < vector < int >>& meetings ) { priority_queue < int , vector < int > , greater < int >> idle ; priority_queue < pii , vector < pii > , greater < pii >> busy ; for ( int i = 0 ; i < n ; ++ i ) idle . push ( i ); vector < int > cnt ( n ); sort ( meetings . begin (), meetings . end ()); for ( auto & v : meetings ) { int s = v [ 0 ], e = v [ 1 ]; while ( ! busy . empty () && busy . top (). first <= s ) { idle . push ( busy . top (). second ); busy . pop (); } int i = 0 ; if ( ! idle . empty ()) { i = idle . top (); idle . pop (); busy . push ({ e , i }); } else { auto x = busy . top (); busy . pop (); i = x . second ; busy . push ({ x . first + e - s , i }); } ++ cnt [ i ]; } int ans = 0 ; for ( int i = 0 ; i < n ; ++ i ) { if ( cnt [ ans ] < cnt [ i ]) { ans = i ; } } return ans ; } };class Solution: def mostBooked(self, n: int, meetings: List[List[int]]) -> int: meetings . sort() busy = [] idle = list(range(n)) heapify(idle) cnt = [0] * n for s, e in meetings: while busy and busy[0][0] <= s: heappush(idle, heappop(busy)[1]) if idle: i = heappop(idle) cnt[i] += 1 heappush(busy, (e, i)) else: a, i = heappop(busy) cnt[i] += 1 heappush(busy, (a + e - s, i)) ans = 0 for i, v in enumerate(cnt): if cnt[ans] < v: ans = i return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.