The Number of the Smallest Unoccupied Chair
Problem
There is a party where n friends numbered from 0 to n - 1 are attending. There is an infinite number of chairs in this party that are numbered from 0 to infinity. When a friend arrives at the party, they sit on the unoccupied chair with the smallest number.
- For example, if chairs
0,1, and5are occupied when a friend comes, they will sit on chair number2.
When a friend leaves the party, their chair becomes unoccupied at the moment they leave. If another friend arrives at that same moment, they can sit in that chair.
You are given a 0-indexed 2D integer array times where times[i] = [arrivali, leavingi], indicating the arrival and leaving times of the ith friend respectively, and an integer targetFriend. All arrival times are distinct.
Return the chair number that the friend numbered targetFriend will sit on.
Examples
Example 1:
Input: times = [[1,4],[2,3],[4,6]], targetFriend = 1
Output: 1
Explanation:
- Friend 0 arrives at time 1 and sits on chair 0.
- Friend 1 arrives at time 2 and sits on chair 1.
- Friend 1 leaves at time 3 and chair 1 becomes empty.
- Friend 0 leaves at time 4 and chair 0 becomes empty.
- Friend 2 arrives at time 4 and sits on chair 0.
Since friend 1 sat on chair 1, we return 1.
Example 2:
Input: times = [[3,10],[1,5],[2,6]], targetFriend = 0
Output: 2
Explanation:
- Friend 1 arrives at time 1 and sits on chair 0.
- Friend 2 arrives at time 2 and sits on chair 1.
- Friend 0 arrives at time 3 and sits on chair 2.
- Friend 1 leaves at time 5 and chair 0 becomes empty.
- Friend 2 leaves at time 6 and chair 1 becomes empty.
- Friend 0 leaves at time 10 and chair 2 becomes empty.
Since friend 0 sat on chair 2, we return 2.
Solution
Video explanation
Here is the video explaining below methods in detail. Please check it out:
<div class="youtube-embed"><iframe src="https://www.youtube.com/embed/3u34LDIEiVA" frameborder="0" allowfullscreen></iframe></div>
Method 1 - Using Map and Priority Queue
Here is the idea:
- Simulate the Chair Allocation: When a friend arrives, we need to find the smallest unoccupied chair. When they leave, that chair becomes available again.
- Two Events:
- Arrival events where a friend occupies the smallest available chair.
- Leaving events where a chair is freed up.
- Sorting: First, sort all events (both arrival and departure) by time. For events happening at the same time, handle departures before arrivals to ensure that any chair vacated is available immediately.
- Data Structures:
- Use a priority queue to keep track of the smallest available chairs.
- Use a map (or hashmap) to associate each friend with their allocated chair.
Here is the approach:
- Parse and sort the events.
- Use a priority queue to manage chair allocation.
- When processing arrivals, allocate the smallest available chair.
- When processing departures, mark the chair as available again.
- Return the chair number of the target friend.
Code
Java
public class Solution {
public int smallestChair(int[][] times, int targetFriend) {
// List to hold all events
List<int[]> events = new ArrayList<>();
// Create events for arrivals and departures
for (int i = 0; i < times.length; i++) {
events.add(new int[]{times[i][0], 1, i}); // Arrival event
events.add(new int[]{times[i][1], 0, i}); // Departure event
}
// Sort events: first by time, then by type (departure before arrival if same time)
events.sort((a, b) -> {
if (a[0] == b[0]) {
return Integer.compare(a[1], b[1]);
} else {
return Integer.compare(a[0], b[0]);
}
});
// Priority queue to track smallest available chairs
PriorityQueue<Integer> availableChairs = new PriorityQueue<>();
for (int i = 0; i < times.length; i++) {
availableChairs.add(i);
}
// Map to store which chair a friend is sitting on
Map<Integer, Integer> friendChairMap = new HashMap<>();
// Process each event
for (int[] event : events) {
int time = event[0];
boolean isArrival = event[1] == 1;
int friendIdx = event[2];
if (isArrival) {
int chair = availableChairs.poll(); // Get the smallest available chair
friendChairMap.put(friendIdx, chair); // Assign the chair to the friend
if (friendIdx == targetFriend) {
return chair; // If it's the target friend, return the chair number
}
} else {
availableChairs.add(friendChairMap.get(friendIdx)); // Free the chair
}
}
return -1; // Should not reach here if inputs are valid
}
}
Python
class Solution:
def smallestChair(self, times: List[List[int]], targetFriend: int) -> int:
# List to hold all events
events = []
# Create events for arrivals and departures
for i, (arrive, leave) in enumerate(times):
events.append((arrive, True, i)) # Arrival event
events.append((leave, False, i)) # Departure event
# Sort events: first by time, then by type (departure before arrival if same time)
events.sort(key=lambda x: (x[0], not x[1]))
# Priority queue to track smallest available chairs
available_chairs = list(range(len(times)))
heapq.heapify(available_chairs)
# Dictionary to store which chair a friend is sitting on
friendChairMap = {}
# Process each event
for time, is_arrival, friend_idx in events:
if is_arrival:
chair = heapq.heappop(available_chairs) # Get the smallest available chair
friendChairMap[friend_idx] = chair # Assign the chair to the friend
if friend_idx == targetFriend:
return chair # If it's the target friend, return the chair number
else:
heapq.heappush(available_chairs, friendChairMap[friend_idx]) # Free the chair
# Should not reach here if inputs are valid
return -1
Complexity
- ⏰ Time complexity:
O(n log n), due to sorting events and operations on the priority queue. - 🧺 Space complexity:
O(n)for maintaining the priority queue and mapping.
Method 2 - Using Two Priority Queues
Here are the steps:
- We need to sort the times based on arrival time, which means we will loose the order of friends. So, before that, store the
arrivalTimeoftargetFriend, since all the the arrival times are distinct this can be used to identify the targetFriend in sorted array. - Store all the available seats(one for each friend) in
minHeapas available. Because we need to choose the min available chair number - Sort the times array based on arrival time.
- Maintain another minHeap to store leavingTime and occupiedSeat.
- loop through sorted array
- remove all the friends from the heap whose leavingTime is less than or equal to current arraival time and add the occupied seats back to available heap.
- if the current start is equal to targetStart return the min available seat.
- else add the current leaving time and min available seat to heap.
Code
Java
class Solution {
public int smallestChair(int[][] times, int targetFriend) {
// Time at which targetFriend arrives
int targetStart = times[targetFriend][0];
// Sort the times array based on arrival times
Arrays.sort(times, (a, b) -> a[0] - b[0]);
// Priority queue (min-heap) to store available chairs
PriorityQueue<Integer> available = new PriorityQueue<>();
for (int i = 0; i < times.length; ++i) {
available.offer(i);
}
// Priority queue (min-heap) to store departure times and associated chairs
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
// Process each friend based on sorted arrival times
for (int i = 0; i < times.length; ++i) {
// Free up chairs based on departure times before the current arrival time
while (!pq.isEmpty() && pq.peek()[0] <= times[i][0]) {
available.offer(pq.poll()[1]);
}
// If we reach the target friend, break and return the smallest available chair
if (times[i][0] == targetStart) {
break;
}
// Assign the smallest available chair to the current friend and add their departure time to the priority queue
pq.offer(new int[]{times[i][1], available.poll()});
}
// Return the smallest available chair for the target friend
return available.peek();
}
}
Python
class Solution:
def smallestChair(self, times: List[List[int]], targetFriend: int) -> int:
# Time at which targetFriend arrives
targetStart = times[targetFriend][0]
# Sort the times array based on arrival times
times.sort(key=lambda x: x[0])
# Priority queue (min-heap) to store available chairs
available = list(range(len(times)))
heapq.heapify(available)
# Priority queue (min-heap) to store departure times and associated chairs
pq = []
# Process each friend based on sorted arrival times
for i in range(len(times)):
# Free up chairs based on departure times before the current arrival time
while pq and pq[0][0] <= times[i][0]:
heapq.heappush(available, heapq.heappop(pq)[1])
# If we reach the target friend, break and return the smallest available chair
if times[i][0] == targetStart:
break
# Assign the smallest available chair to the current friend and add their departure time to the priority queue
heapq.heappush(pq, (times[i][1], heapq.heappop(available)))
# Return the smallest available chair for the target friend
return available[0]
Complexity
- ⏰ Time complexity:
O(n log n), where n is the number of friends.- Sorting the
timesarray:O(n log n) - Each friend is processed once, and each operation on the priority queues (
availableandpq) isO(log n). - Each chair is pushed and popped from the heaps at most once per friend, so total heap operations are
O(n log n).
- Sorting the
- 🧺 Space complexity:
O(n)- The
availablemin-heap stores up tonchair numbers. - The
pqmin-heap stores up tondeparture events. - No extra space beyond these two heaps and a few variables.
- The