2402. Meeting Rooms III - Explanation

Problem Link

Description

There are n rooms numbered from 0 to n - 1. You are given a 2D integer array meetings where meetings[i] = [start[i], end[i]] means that a meeting will be held during the half-closed time interval [start[i], end[i]). All the values of start[i] are unique.

Meetings are allocated to rooms in the following manner:

  1. Each meeting will take place in the unused room with the lowest number.

  2. 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.

  3. 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 = [[1,10],[2,10],[3,10],[4,10]]

Output: 0

Explanation:

  • At time 1, the room with number 0 is available, the first meeting is allocated to it.
  • At time 2, the room with number 1 is available, the second meeting is allocated to it.
  • At time 10, the rooms with numbers 0 and 1 becomes unused, the third meeting is allocated to room number 0 and fourth meeting is allocated to room number 1.

The room with most meetings held and have lowest number is 0.

Example 2:

Input: n = 3, meetings = [[1,20],[2,10],[3,5],[6,8],[4,9]]

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 as it is lowest.

Constraints:

  • 1 <= n <= 100
  • 1 <= meetings.length <= 100,000
  • meetings[i].length == 2
  • 0 <= start[i] < end[i] <= 500,000
  • All the values of start[i] are unique.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Sorting - Ordering meetings by start time to process them chronologically
  • Heap / Priority Queue - Efficiently finding the minimum element (earliest available room or end time)
  • Simulation - Tracking state changes over time as meetings are assigned and rooms become available
  • Greedy Selection - Choosing the optimal room based on availability and room number constraints

1. Sorting + Brute Force

Intuition

Meetings must be processed in order of their start times. For each meeting, we check rooms in order from 0 to n - 1. If a room is free by the meeting's start time, we assign the meeting there. Otherwise, we find the room that becomes free earliest and delay the meeting until that room is available. We track how many meetings each room hosts and return the room with the most meetings, breaking ties by smallest room number.

Algorithm

  1. Sort meetings by start time.
  2. Initialize arrays rooms (end times) and meeting_count, both of size n.
  3. For each meeting (start, end):
    • Scan rooms 0 to n - 1. If rooms[i] <= start, assign the meeting: set rooms[i] = end, increment meeting_count[i], and move to the next meeting.
    • While scanning, track min_room as the room with the earliest end time.
    • If no room is free, assign to min_room: set rooms[min_room] += (end - start) and increment meeting_count[min_room].
  4. Return the index with the maximum count (smallest index on ties).
class Solution:
    def mostBooked(self, n: int, meetings: List[List[int]]) -> int:
        meetings.sort()
        rooms = [0] * n # end time of meetings in rooms
        meeting_count = [0] * n

        for s, e in meetings:
            min_room = 0
            found = False
            for i in range(n):
                if rooms[i] <= s:
                    found = True
                    meeting_count[i] += 1
                    rooms[i] = e
                    break

                if rooms[min_room] > rooms[i]:
                    min_room = i
            if found:
                continue
            meeting_count[min_room] += 1
            rooms[min_room] += e - s

        return meeting_count.index(max(meeting_count))

Time & Space Complexity

  • Time complexity: O(mlogm+nm)O(m\log m + n * m)
  • Space complexity: O(n)O(n)

Where nn is the number of rooms and mm is the number of meetings.


2. Two Min-Heaps

Intuition

Scanning all rooms for each meeting is slow. Instead, we use two min-heaps: one for available rooms (ordered by room number) and one for rooms in use (ordered by end time, then room number). When a meeting starts, we first move all rooms whose meetings have ended back to the available heap. If available is empty, we pop the earliest-ending room, delay the meeting, and push that room back to available. Then we pop the smallest available room, assign the meeting, and push it to the used heap.

Algorithm

  1. Sort meetings by start time.
  2. Initialize a min-heap available with room numbers 0 to n - 1.
  3. Initialize an empty min-heap used storing (end_time, room).
  4. Initialize an array count of size n.
  5. For each (start, end):
    • While used is not empty and used[0].end_time <= start, pop from used and push the room to available.
    • If available is empty, pop (end_time, room) from used, update end = end_time + (end - start), and push room to available.
    • Pop room from available, push (end, room) to used, and increment count[room].
  6. Return the index with maximum count.
class Solution:
    def mostBooked(self, n: int, meetings: List[List[int]]) -> int:
        meetings.sort()
        available = [i for i in range(n)]  # Min heap for available rooms
        used = []  # Min heap for used rooms [(end_time, room_number)]
        count = [0] * n  # Count of meetings for each room

        for start, end in meetings:
            while used and used[0][0] <= start:
                _, room = heapq.heappop(used)
                heapq.heappush(available, room)

            if not available:
                end_time, room = heapq.heappop(used)
                end = end_time + (end - start)
                heapq.heappush(available, room)

            room = heapq.heappop(available)
            heapq.heappush(used, (end, room))
            count[room] += 1

        return count.index(max(count))

Time & Space Complexity

  • Time complexity: O(mlogm+mlogn)O(m\log m + m \log n)
  • Space complexity: O(n)O(n)

Where nn is the number of rooms and mm is the number of meetings.


3. One Min-Heap

Intuition

We can simplify to a single heap that tracks (end_time, room). Initially all rooms have end time 0. Before assigning a meeting, we update any rooms whose end time is before the meeting's start to have end time equal to the start (they become available). Then we pop the room with the smallest end time (and smallest room number on ties), assign the meeting, and push the room back with the new end time.

Algorithm

  1. Sort meetings by start time.
  2. Initialize a min-heap available with (0, room) for each room 0 to n - 1.
  3. Initialize an array count of size n.
  4. For each (start, end):
    • While available[0].end_time < start, pop (_, room) and push (start, room).
    • Pop (end_time, room) from the heap.
    • Push (end_time + (end - start), room) back to the heap.
    • Increment count[room].
  5. Return the index with maximum count.
class Solution:
    def mostBooked(self, n: int, meetings: List[List[int]]) -> int:
        meetings.sort()
        available = []
        count = [0] * n

        for i in range(n):
            heapq.heappush(available, (0, i))

        for start, end in meetings:
            while available and available[0][0] < start:
                end_time, room = heapq.heappop(available)
                heapq.heappush(available, (start, room))

            end_time, room = heapq.heappop(available)
            heapq.heappush(available, (end_time + (end - start), room))
            count[room] += 1

        return count.index(max(count))

Time & Space Complexity

  • Time complexity:
    • O(mlogm+mlogn)O(m \log m + m \log n) time in average case.
    • O(mlogm+mn)O(m \log m + m * n) time in worst case.
  • Space complexity: O(n)O(n)

Where nn is the number of rooms and mm is the number of meetings.


Common Pitfalls

Forgetting to Sort Meetings by Start Time

Meetings must be processed in chronological order. Without sorting, you cannot correctly determine which room becomes available first or when meetings need to be delayed.

Integer Overflow When Calculating Delayed End Times

When meetings are delayed, end times can grow very large. Using int instead of long can cause overflow. Always use long or equivalent 64-bit integer types for end times.

Incorrect Tie-Breaking for Room Selection

When multiple rooms are available, you must choose the room with the smallest index. When multiple rooms become free at the same time, you must also prefer the smaller index. Failing to implement this tie-breaking correctly leads to wrong answers.

Not Preserving Meeting Duration When Delaying

When a meeting is delayed because no room is available, the meeting duration must remain the same. The new end time should be earliest_available_time + (original_end - original_start), not simply the original end time.

Confusing Available vs Used Room Logic with Two Heaps

When using two heaps, it is easy to forget to move rooms from the used heap back to the available heap when their meetings end. Always check and transfer rooms whose end time is at or before the current meeting's start time before attempting to assign a room.