Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1942. The Number of the Smallest Unoccupied Chair - Leetcode Solution

Code Implementation

import heapq

class Solution:
    def smallestChair(self, times, targetFriend):
        n = len(times)
        arrivals = []
        for i, (arr, leave) in enumerate(times):
            arrivals.append((arr, i, leave))
        arrivals.sort()  # Sort by arrival time

        occupied = []  # Min-heap of (leave_time, chair_number)
        free_chairs = []  # Min-heap of available chair numbers
        next_chair = 0  # Next new chair to use

        for arr_time, friend, leave_time in arrivals:
            # Free up chairs for friends who have already left
            while occupied and occupied[0][0] <= arr_time:
                left_time, chair_num = heapq.heappop(occupied)
                heapq.heappush(free_chairs, chair_num)
            # Assign the smallest available chair
            if free_chairs:
                chair = heapq.heappop(free_chairs)
            else:
                chair = next_chair
                next_chair += 1
            heapq.heappush(occupied, (leave_time, chair))
            if friend == targetFriend:
                return chair
    
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

class Solution {
public:
    int smallestChair(vector<vector<int>>& times, int targetFriend) {
        int n = times.size();
        vector<tuple<int, int, int>> arrivals;
        for (int i = 0; i < n; ++i) {
            arrivals.emplace_back(times[i][0], i, times[i][1]);
        }
        sort(arrivals.begin(), arrivals.end());

        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> occupied;
        priority_queue<int, vector<int>, greater<int>> freeChairs;
        int nextChair = 0;

        for (auto& [arr, friendIdx, leave] : arrivals) {
            while (!occupied.empty() && occupied.top().first <= arr) {
                freeChairs.push(occupied.top().second);
                occupied.pop();
            }
            int chair;
            if (!freeChairs.empty()) {
                chair = freeChairs.top();
                freeChairs.pop();
            } else {
                chair = nextChair++;
            }
            occupied.push({leave, chair});
            if (friendIdx == targetFriend)
                return chair;
        }
        return -1;
    }
};
    
import java.util.*;

class Solution {
    public int smallestChair(int[][] times, int targetFriend) {
        int n = times.length;
        List<int[]> arrivals = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            arrivals.add(new int[]{times[i][0], i, times[i][1]});
        }
        arrivals.sort(Comparator.comparingInt(a -> a[0]));

        PriorityQueue<int[]> occupied = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        PriorityQueue<Integer> freeChairs = new PriorityQueue<>();
        int nextChair = 0;

        for (int[] arr : arrivals) {
            int arrTime = arr[0], friendIdx = arr[1], leaveTime = arr[2];
            while (!occupied.isEmpty() && occupied.peek()[0] <= arrTime) {
                freeChairs.offer(occupied.poll()[1]);
            }
            int chair;
            if (!freeChairs.isEmpty()) {
                chair = freeChairs.poll();
            } else {
                chair = nextChair++;
            }
            occupied.offer(new int[]{leaveTime, chair});
            if (friendIdx == targetFriend)
                return chair;
        }
        return -1;
    }
}
    
class MinHeap {
    constructor(compare) {
        this.data = [];
        this.compare = compare;
    }
    push(item) {
        this.data.push(item);
        this._siftUp();
    }
    pop() {
        if (this.data.length === 1) return this.data.pop();
        const top = this.data[0];
        this.data[0] = this.data.pop();
        this._siftDown();
        return top;
    }
    peek() {
        return this.data[0];
    }
    size() {
        return this.data.length;
    }
    _siftUp() {
        let i = this.data.length - 1;
        while (i > 0) {
            let p = Math.floor((i - 1) / 2);
            if (this.compare(this.data[i], this.data[p]) < 0) {
                [this.data[i], this.data[p]] = [this.data[p], this.data[i]];
                i = p;
            } else break;
        }
    }
    _siftDown() {
        let i = 0;
        const n = this.data.length;
        while (true) {
            let l = 2 * i + 1, r = 2 * i + 2, smallest = i;
            if (l < n && this.compare(this.data[l], this.data[smallest]) < 0) smallest = l;
            if (r < n && this.compare(this.data[r], this.data[smallest]) < 0) smallest = r;
            if (smallest === i) break;
            [this.data[i], this.data[smallest]] = [this.data[smallest], this.data[i]];
            i = smallest;
        }
    }
}

var smallestChair = function(times, targetFriend) {
    let arrivals = times.map((time, i) => [time[0], i, time[1]]);
    arrivals.sort((a, b) => a[0] - b[0]);
    let occupied = new MinHeap((a, b) => a[0] - b[0]);
    let freeChairs = new MinHeap((a, b) => a - b);
    let nextChair = 0;

    for (let [arrTime, friend, leaveTime] of arrivals) {
        while (occupied.size() > 0 && occupied.peek()[0] <= arrTime) {
            freeChairs.push(occupied.pop()[1]);
        }
        let chair;
        if (freeChairs.size() > 0) {
            chair = freeChairs.pop();
        } else {
            chair = nextChair++;
        }
        occupied.push([leaveTime, chair]);
        if (friend === targetFriend) return chair;
    }
    return -1;
};
    

Problem Description

You are given an array times where times[i] = [arrive_i, leave_i] describes the arrival and leaving times of the i-th friend at a party. Each friend takes a seat when they arrive and leaves it when they go. There are infinite chairs, numbered from 0 upwards.

When a friend arrives, they always take the smallest-numbered unoccupied chair. If multiple friends arrive at the same time, they are assigned seats in order of increasing i (their index in times).

You are also given an integer targetFriend. Your task is to return the number of the chair that the targetFriend will sit in.

  • Each chair can only be used by one friend at a time.
  • Friends do not share or reuse chairs simultaneously.
  • There is always exactly one answer for the given input.

Thought Process

At first glance, the problem seems approachable by simulating the process: for every friend's arrival, scan the list of chairs to find the smallest one not currently occupied. However, with many friends and overlapping intervals, this naïve approach could become slow and inefficient.

To optimize, we need a way to efficiently track which chairs are available at any given time and always be able to quickly select the smallest available chair. We also need to handle friends leaving and freeing up their chairs.

The key is to process events (arrivals and departures) in chronological order, so we always know the current state of chair occupancy. Using data structures like heaps (priority queues) can help us efficiently manage available chairs and the timing of departures.

Solution Approach

  • Step 1: Sort Arrivals
    • Sort all friends by their arrival time. If two friends arrive at the same time, process the one with the smaller index first, as per the problem statement.
  • Step 2: Track Chair Usage
    • Use a min-heap to keep track of which chairs are currently occupied, storing pairs of (leave time, chair number). This allows us to efficiently free up chairs as friends leave.
    • Use a second min-heap to keep track of all available (free) chair numbers. This lets us always pick the smallest-numbered available chair in O(log n) time.
  • Step 3: Assign Chairs
    • For each arriving friend, before assigning a chair, check if any friends have left (i.e., their leave time is ≤ the current arrival time). If so, free up their chairs by moving them to the available chairs heap.
    • If there are any free chairs, assign the smallest-numbered one. If not, assign the next new chair number.
    • Push the new occupation (leave time, chair number) into the occupied chairs heap.
  • Step 4: Return Result
    • As soon as we process the targetFriend, return the chair assigned to them.

This approach ensures that both assigning and releasing chairs are handled efficiently, and we always meet the problem's requirement of giving each friend the smallest available chair at their arrival time.

Example Walkthrough

Let's consider times = [[1,4],[2,3],[4,6]] and targetFriend = 1.

  • Friend 0: arrives at 1, leaves at 4
  • Friend 1: arrives at 2, leaves at 3
  • Friend 2: arrives at 4, leaves at 6
  1. At time 1: Only friend 0 arrives. All chairs are free, so friend 0 gets chair 0. Chair 0 is now occupied until time 4.
  2. At time 2: Friend 1 arrives. Chair 0 is still occupied, so friend 1 gets chair 1 (the next smallest available). Chair 1 is now occupied until time 3.
  3. At time 3: Friend 1 leaves, so chair 1 becomes available.
  4. At time 4: Friend 2 arrives. Friend 0 leaves, so chair 0 becomes available. Both chairs 0 and 1 are now free, but friend 2 gets chair 0 (the smallest). Chair 0 is now occupied until time 6.

Since targetFriend = 1, and they were assigned chair 1 at their arrival, the answer is 1.

Time and Space Complexity

  • Brute-force Approach:
    • For each friend, scan all chairs to find the smallest unoccupied one. This can take O(n^2) time for n friends.
    • Space complexity is O(n) for storing chair assignments.
  • Optimized Heap Approach (this solution):
    • Sorting the arrivals takes O(n log n) time.
    • Each chair assignment and release involves heap operations, each O(log n), for a total of O(n log n) time.
    • Space complexity is O(n) for the heaps and event storage.

Overall, the optimized solution is efficient and suitable for large inputs.

Summary

The key insight is to process arrivals and departures in chronological order, using heaps to efficiently track available and occupied chairs. This ensures that each friend gets the smallest possible chair number at their arrival, and that released chairs are quickly reused. The approach is both efficient and elegant, leveraging priority queues to maintain the problem's constraints with minimal overhead.