leetcode
leetcode 751 ~ 800
考场就座

考场就座

难度:

标签:

题目描述

代码结果

运行时间: 64 ms, 内存: 0.0 MB


/*
思路:
1. 使用一个 TreeSet 记录已经被占据的座位,以保持有序。
2. 每次调用 seat() 时,找到能够使离他最近的人之间的距离最大化的座位。
3. 使用 leave(p) 函数将座位 p 标记为空。
*/

import java.util.TreeSet;

public class ExamRoom {
    private int N;
    private TreeSet<Integer> seats;

    public ExamRoom(int N) {
        this.N = N;
        this.seats = new TreeSet<>();
    }

    public int seat() {
        if (seats.isEmpty()) {
            seats.add(0);
            return 0;
        }

        int maxDist = seats.first();
        int seat = 0;
        Integer prev = null;

        for (int s : seats) {
            if (prev != null) {
                int dist = (s - prev) / 2;
                if (dist > maxDist) {
                    maxDist = dist;
                    seat = prev + dist;
                }
            }
            prev = s;
        }

        if (N - 1 - seats.last() > maxDist) {
            seat = N - 1;
        }

        seats.add(seat);
        return seat;
    }

    public void leave(int p) {
        seats.remove(p);
    }
}

解释

方法:

这个题解使用了优先队列(最大堆)来快速找到最优的座位。首先,每个区间(即连续的空座位)被标记为一个三元组 [-priority, first, last],其中 priority 是该区间的优先级,first 和 last 分别是区间的起始和结束座位。如果区间是开头或结尾的座位,优先级就是区间长度;否则,优先级是区间长度除以二的商。这样设计是为了优先坐在最中间的位置,以最大化与最近的人的距离。当座位被选取时,相关区间会被拆分并重新加入堆中,如果座位 p 被释放,相邻的空座位区间会合并并更新。

时间复杂度:

O(log n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在初始化ExamRoom时,整个座位区间被设定为从0到N-1?这是否意味着开始时考场是完全空的?
是的,初始化ExamRoom时整个座位区间设定为从0到N-1表示开始时考场是完全空的。这样的设定是为了模拟考场在开始前没有任何学生入座的情况,从而允许算法从一个完整且未被占用的座位区间开始进行座位分配。这种方式简化了座位的管理和分配过程,因为它从最大可能的空座位区间开始,并逐步根据实际座位的选择和释放进行调整。
🦆
在put_segment函数中,为什么边界区间的优先级是整个区间长度,而非边界区间的优先级是区间长度除以二?这样的处理对于找到最优座位的影响是什么?
在put_segment函数中,边界区间(即第一个或最后一个座位的区间)的优先级设定为整个区间长度,而非边界区间(即中间的座位区间)的优先级设置为区间长度除以二的原因在于优化座位的选择,使人与人之间的距离最大化。边界区间的优先级较高是因为在边界坐下时,一个方向上没有其他人,从而可以确保一侧的最大距离。对于中间的区间,通过取区间长度的一半作为优先级,可以确保选择座位时人们尽可能处于区间的中间位置,从而最大化与最近的两侧人的距离。这种处理有助于在考场中尽可能地避免紧挨着坐,提高考场的使用效率和考生的舒适度。
🦆
在seat函数中,如何确保从堆中弹出的区间是有效的?如果堆中的区间因为之前的leave操作而变得无效,这种情况是如何处理的?
在seat函数中,确保从堆中弹出的区间是有效的通过在每个区间元组中添加一个布尔值来实现,该布尔值标记区间是否有效。当执行leave操作时,如果某个座位被释放,与该座位相邻的区间可能会被合并,这时原有的区间会被标记为无效并从有效映射中删除。从堆中弹出区间后,会检查这个标记来确定区间是否仍然有效。如果区间无效(即布尔值为False),则继续从堆中弹出下一个区间,直到找到一个有效的区间。这种方法确保了即使区间在堆中排序后状态发生变化,座位分配的逻辑仍然能够正确地处理有效区间,避免了无效数据的干扰。

相关问题

到最近的人的最大距离

给你一个数组 seats 表示一排座位,其中 seats[i] = 1 代表有人坐在第 i 个座位上,seats[i] = 0 代表座位 i 上是空的(下标从 0 开始)。

至少有一个空座位,且至少有一人已经坐在座位上。

亚历克斯希望坐在一个能够使他与离他最近的人之间的距离达到最大化的座位上。

返回他到离他最近的人的最大距离。

 

示例 1:

输入:seats = [1,0,0,0,1,0,1]
输出:2
解释:
如果亚历克斯坐在第二个空位(seats[2])上,他到离他最近的人的距离为 2 。
如果亚历克斯坐在其它任何一个空位上,他到离他最近的人的距离为 1 。
因此,他到离他最近的人的最大距离是 2 。 

示例 2:

输入:seats = [1,0,0,0]
输出:3
解释:
如果亚历克斯坐在最后一个座位上,他离最近的人有 3 个座位远。
这是可能的最大距离,所以答案是 3 。

示例 3:

输入:seats = [0,1]
输出:1

 

提示:

  • 2 <= seats.length <= 2 * 104
  • seats[i]01
  • 至少有一个 空座位
  • 至少有一个 座位上有人