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

派对椅子分配问题详解:最小编号空椅子分配算法

题目描述

假设有 n 个朋友,他们正在举办一个派对。朋友编号从 0n-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递增,常见的做法是:

  • 维护一个小根堆保存空闲椅子编号,每次分配时从这里取最小编号椅子。
  • 维护一个小根堆保存正在使用的椅子和对应的离开时间,当时间达到朋友离开时间时释放椅子。

解题方法

具体步骤如下:

  1. 排序
    按照朋友的到达时间对 times 进行排序,确保按照时间顺序处理。
  2. 数据结构
    • 一个小根堆 free_chairs 维护空闲椅子编号。
    • 一个小根堆 used_chairs 维护 (离开时间, 椅子编号),表示当前被占用的椅子。
  3. 模拟过程
    遍历每个朋友的到达时间:
    • 先检查 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。


总结

这道题巧妙结合了“区间时间管理”和“最小堆”两大数据结构知识:

  • 通过时间排序和模拟来保证事件顺序正确。
  • 通过两个堆动态维护椅子的分配和释放。
  • 利用最小堆保证分配的椅子编号始终是最小可用的。

掌握这类问题的处理技巧,对类似资源调度、区间分配等题目大有裨益。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值