派对椅子分配问题详解:最小编号空椅子分配算法
题目描述
假设有 n 个朋友,他们正在举办一个派对。朋友编号从 0
到 n-1
,派对中有无数张编号从 0
开始的椅子。每当一个朋友到达派对时,他会占据编号最小且未被占据的椅子。
具体规则是:
- 当朋友到达时,选择编号最小的空椅子坐下。
- 当朋友离开时,他占据的椅子马上变为空闲状态,其他新到的朋友可以立即使用这张椅子。
- 给定一个二维数组
times
,其中times[i] = [arrivali, leavingi]
表示第i
个朋友到达和离开的时间,且所有到达时间互不相同。 - 给定一个整数
targetFriend
,请返回编号为targetFriend
的朋友占据的椅子编号。
例子说明
假设:
- 朋友 0 到达时间是 1,离开时间是 4
- 朋友 1 到达时间是 2,离开时间是 3
- 朋友 2 到达时间是 4,离开时间是 6
- 目标朋友是 1
按规则,朋友 0 到达时占用椅子 0;朋友 1 到达时椅子 1 未占用,朋友 1 占用椅子 1;朋友 1 离开时,椅子 1 变空。朋友 2 到达时,椅子 1 是最小空椅子,朋友 2 占用椅子 1。
返回目标朋友 1 占用的椅子编号为 1。
解题分析
这题本质是一个动态的“区间资源分配”问题。朋友的到达和离开时间构成一系列时间区间,椅子的分配必须遵守最小编号优先且能复用空闲椅子的规则。
关键点在于:
- 如何有效管理椅子的空闲和占用状态?
- 如何高效找到最小编号的空闲椅子?
- 如何在朋友离开时及时释放椅子?
因为有无数椅子且编号从0递增,常见的做法是:
- 维护一个小根堆保存空闲椅子编号,每次分配时从这里取最小编号椅子。
- 维护一个小根堆保存正在使用的椅子和对应的离开时间,当时间达到朋友离开时间时释放椅子。
解题方法
具体步骤如下:
- 排序
按照朋友的到达时间对times
进行排序,确保按照时间顺序处理。 - 数据结构
- 一个小根堆
free_chairs
维护空闲椅子编号。 - 一个小根堆
used_chairs
维护(离开时间, 椅子编号)
,表示当前被占用的椅子。
- 一个小根堆
- 模拟过程
遍历每个朋友的到达时间:- 先检查
used_chairs
,释放所有已经离开的朋友的椅子,把椅子编号放回free_chairs
。 - 分配椅子:
- 如果
free_chairs
不为空,弹出最小编号椅子给当前朋友。 - 否则,使用一个新的椅子编号(从0开始递增)。
- 如果
- 将当前朋友的
(离开时间, 椅子编号)
放入used_chairs
。 - 如果当前朋友是目标朋友,立即返回其椅子编号。
- 先检查
代码实现
import heapq
from typing import List
class Solution:
def smallestChair(self, times: List[List[int]], targetFriend: int) -> int:
n = len(times)
# 添加朋友ID,排序以便按到达时间处理
events = [(arr, leave, i) for i, (arr, leave) in enumerate(times)]
events.sort(key=lambda x: x[0])
free_chairs = []
next_chair = 0
used_chairs = []
friend_chair = {}
for arr, leave, friend_id in events:
# 释放已离开的椅子
while used_chairs and used_chairs[0][0] <= arr:
_, chair_num = heapq.heappop(used_chairs)
heapq.heappush(free_chairs, chair_num)
# 分配椅子
if free_chairs:
chair = heapq.heappop(free_chairs)
else:
chair = next_chair
next_chair += 1
friend_chair[friend_id] = chair
heapq.heappush(used_chairs, (leave, chair))
if friend_id == targetFriend:
return chair
复杂度分析
- 排序:
O(n log n)
,n
是朋友数量。 - 遍历时,每个朋友的到达和离开事件最多入堆和出堆一次,堆操作复杂度为
O(log n)
。 - 总体时间复杂度:
O(n log n)
。 - 空间复杂度:
O(n)
,用于堆和记录椅子状态。
示例演示
以输入:
times = [[1,4],[2,3],[4,6]]
targetFriend = 1
流程:
- 朋友 0 到达时间 1,占用椅子 0。
- 朋友 1 到达时间 2,椅子 0 被占,空闲椅子无,分配椅子 1。
- 朋友 1 离开时间 3,椅子 1 释放。
- 朋友 2 到达时间 4,先释放朋友 0 椅子(时间 4 离开,释放椅子 0),椅子 0 和 1 都空闲,选椅子 0(最小编号)。
- 朋友 2 占用椅子 0。
目标朋友 1 占用椅子 1,函数返回 1。
总结
这道题巧妙结合了“区间时间管理”和“最小堆”两大数据结构知识:
- 通过时间排序和模拟来保证事件顺序正确。
- 通过两个堆动态维护椅子的分配和释放。
- 利用最小堆保证分配的椅子编号始终是最小可用的。
掌握这类问题的处理技巧,对类似资源调度、区间分配等题目大有裨益。