LeetCode 1942.最小未被占据椅子的编号

有 n 个朋友在举办一个派对,这些朋友从 0 到 n - 1 编号。派对里有 无数 张椅子,编号为 0 到 infinity 。当一个朋友到达派对时,他会占据 编号最小 且未被占据的椅子。

比方说,当一个朋友到达时,如果椅子 0 ,1 和 5 被占据了,那么他会占据 2 号椅子。

当一个朋友离开派对时,他的椅子会立刻变成未占据状态。如果同一时刻有另一个朋友到达,可以立即占据这张椅子。

给你一个下标从 0 开始的二维整数数组 times ,其中 timesi = arrivali, leavingi 表示第 i 个朋友到达和离开的时刻,同时给你一个整数 targetFriend 。所有到达时间 互不相同 。

请你返回编号为 targetFriend 的朋友占据的 椅子编号 。

示例 1:

输入:times = \[1,4,2,3,4,6], targetFriend = 1

输出:1

解释:

  • 朋友 0 时刻 1 到达,占据椅子 0 。
  • 朋友 1 时刻 2 到达,占据椅子 1 。
  • 朋友 1 时刻 3 离开,椅子 1 变成未占据。
  • 朋友 0 时刻 4 离开,椅子 0 变成未占据。
  • 朋友 2 时刻 4 到达,占据椅子 0 。
    朋友 1 占据椅子 1 ,所以返回 1 。
    示例 2:

输入:times = \[3,10,1,5,2,6], targetFriend = 0

输出:2

解释:

  • 朋友 1 时刻 1 到达,占据椅子 0 。
  • 朋友 2 时刻 2 到达,占据椅子 1 。
  • 朋友 0 时刻 3 到达,占据椅子 2 。
  • 朋友 1 时刻 5 离开,椅子 0 变成未占据。
  • 朋友 2 时刻 6 离开,椅子 1 变成未占据。
  • 朋友 0 时刻 10 离开,椅子 2 变成未占据。
    朋友 0 占据椅子 2 ,所以返回 2 。

提示:

n == times.length

2 <= n <= 104^44

timesi.length == 2

1 <= arrivali < leavingi <= 105^55

0 <= targetFriend <= n - 1

每个 arrivali 时刻 互不相同 。

我们可以用一个变量来记录已经分配的椅子的最大编号,然后用一个小顶堆来管理曾分配出去,但又释放掉的椅子,到达时刻和离开时刻也可以用小顶堆来管理:

cpp 复制代码
class Solution {
public:
    int smallestChair(vector<vector<int>>& times, int targetFriend) {
        vector<pair<int, int>> arrivalTime(times.size());
        for (int i = 0; i < times.size(); ++i) {
            arrivalTime[i] = {times[i][0], i};
        }

        vector<int> emptyChair;
        vector<int> friendToChair(times.size());
        
        make_heap(arrivalTime.begin(), arrivalTime.end(), greater());

        vector<pair<int, int>> leaveTime;
        int minChair = 0;
        int ans = 0;
        for (int i = 0; i <= times.size(); ++i) {
            // 取剩余最先到达的朋友
            int aTime = arrivalTime[0].first;
            // 先把离开的朋友的椅子释放出来
            while (!empty(leaveTime) && leaveTime[0].first <= aTime) {
                int lFriendIdx = leaveTime[0].second;
                int lChair = friendToChair[lFriendIdx];

                emptyChair.push_back(lChair);
                push_heap(emptyChair.begin(), emptyChair.end(), greater());

                pop_heap(leaveTime.begin(), leaveTime.end(), greater());
                leaveTime.pop_back();
            }

            // 分配椅子
            int aChair = -1;
            // 如果已分配过的椅子全都被占用,直接取未分配的最小椅子编号
            if (empty(emptyChair)) {
                aChair = minChair++;
            // 否则取小顶堆顶的椅子
            } else {
                aChair = emptyChair[0];
                pop_heap(emptyChair.begin(), emptyChair.end(), greater());
                emptyChair.pop_back();
            }

            // 找到目标朋友的位置了
            int aFriendIdx = arrivalTime[0].second;
            if (aFriendIdx == targetFriend) {
                ans = aChair;
                break;
            }

            // 将离开时间加入小顶堆
            friendToChair[aFriendIdx] = aChair;
            leaveTime.push_back({times[arrivalTime[0].second][1], aFriendIdx});
            push_heap(leaveTime.begin(), leaveTime.end(), greater());

            // 将到达时间取出小顶堆
            pop_heap(arrivalTime.begin(), arrivalTime.end(), greater());
            arrivalTime.pop_back();
        }

        return ans;
    }
};

如果有n个朋友,则此算法时间复杂度为O(nlogn),空间复杂度为O(n)。