Medium

题目描述

在一个有 n 个座位的考试房间中,座位按顺序编号为 0n - 1

当一个学生进入房间时,他必须坐在能够使与最近的人的距离最大化的座位上。如果有多个这样的座位,他要坐在编号最小的座位上。如果房间里没有人,那么学生就坐在 0 号座位上。

设计一个模拟上述考试房间的类。

实现 ExamRoom 类:

  • ExamRoom(int n) 用座位数 n 初始化考试房间对象。
  • int seat() 返回下一个学生将要坐的座位的标号。
  • void leave(int p) 指示坐在座位 p 上的学生将离开房间。题目保证座位 p 上会有一个学生。

示例 1:

输入:
["ExamRoom", "seat", "seat", "seat", "seat", "leave", "seat"]
[[10], [], [], [], [], [4], []]
输出:
[null, 0, 9, 4, 2, null, 5]

解释:
ExamRoom examRoom = new ExamRoom(10);
examRoom.seat(); // 返回 0,房间里没有人,学生坐在 0 号座位。
examRoom.seat(); // 返回 9,学生坐在最后一个座位 9 号。
examRoom.seat(); // 返回 4,学生坐在座位 4 号。
examRoom.seat(); // 返回 2,学生坐在座位 2 号。
examRoom.leave(4);
examRoom.seat(); // 返回 5,学生坐在座位 5 号。

提示:

  • 1 <= n <= 10^9
  • 题目保证座位 p 上有学生。
  • 最多会对 seatleave 进行 10^4 次调用。

解题思路

解题思路

这道题要求我们设计一个考试房间,学生入座时要选择距离最近的人最远的位置。

核心策略:

  1. 维护已占座位:使用有序集合(如 TreeSet)来维护当前已被占用的座位编号
  2. 找最佳座位:对于每次 seat() 调用,遍历相邻的已占座位之间的间隔,找到能提供最大距离的位置
  3. 边界处理:特别处理房间两端(0号座位前和n-1号座位后)的情况

算法流程:

  • seat():如果房间为空,返回0;否则计算所有可能位置的最小距离,选择最小距离最大的位置
  • leave(p):从已占座位集合中移除座位p

关键点:

  1. 对于两个相邻座位 ab,中间最佳位置是 (a+b)/2,此时距离为 (b-a)/2
  2. 对于房间两端,0号位置的距离是到第一个人的距离,最后位置的距离是到最后一个人的距离
  3. 当距离相等时,选择编号较小的座位

时间复杂度方面,每次 seat() 需要 O(P) 时间(P为已占座位数),leave() 需要 O(log P) 时间。

代码实现

class ExamRoom {
private:
    int n;
    set<int> seated;
    
public:
    ExamRoom(int n) : n(n) {}
    
    int seat() {
        if (seated.empty()) {
            seated.insert(0);
            return 0;
        }
        
        int maxDist = 0;
        int bestSeat = 0;
        
        // Check seat 0
        int firstSeat = *seated.begin();
        if (firstSeat > 0) {
            maxDist = firstSeat;
            bestSeat = 0;
        }
        
        // Check seats between occupied seats
        auto it = seated.begin();
        auto next = it;
        ++next;
        
        while (next != seated.end()) {
            int left = *it;
            int right = *next;
            int dist = (right - left) / 2;
            if (dist > maxDist) {
                maxDist = dist;
                bestSeat = left + dist;
            }
            ++it;
            ++next;
        }
        
        // Check last seat
        int lastSeat = *seated.rbegin();
        if (lastSeat < n - 1) {
            int dist = n - 1 - lastSeat;
            if (dist > maxDist) {
                maxDist = dist;
                bestSeat = n - 1;
            }
        }
        
        seated.insert(bestSeat);
        return bestSeat;
    }
    
    void leave(int p) {
        seated.erase(p);
    }
};
class ExamRoom:

    def __init__(self, n: int):
        self.n = n
        self.seated = set()

    def seat(self) -> int:
        if not self.seated:
            self.seated.add(0)
            return 0
        
        seated_list = sorted(self.seated)
        max_dist = 0
        best_seat = 0
        
        # Check seat 0
        if seated_list[0] > 0:
            max_dist = seated_list[0]
            best_seat = 0
        
        # Check seats between occupied seats
        for i in range(len(seated_list) - 1):
            left = seated_list[i]
            right = seated_list[i + 1]
            dist = (right - left) // 2
            if dist > max_dist:
                max_dist = dist
                best_seat = left + dist
        
        # Check last seat
        if seated_list[-1] < self.n - 1:
            dist = self.n - 1 - seated_list[-1]
            if dist > max_dist:
                max_dist = dist
                best_seat = self.n - 1
        
        self.seated.add(best_seat)
        return best_seat

    def leave(self, p: int) -> None:
        self.seated.remove(p)
public class ExamRoom {
    private int n;
    private SortedSet<int> seated;
    
    public ExamRoom(int n) {
        this.n = n;
        this.seated = new SortedSet<int>();
    }
    
    public int Seat() {
        if (seated.Count == 0) {
            seated.Add(0);
            return 0;
        }
        
        var seatedList = seated.ToList();
        int maxDist = 0;
        int bestSeat = 0;
        
        // Check seat 0
        if (seatedList[0] > 0) {
            maxDist = seatedList[0];
            bestSeat = 0;
        }
        
        // Check seats between occupied seats
        for (int i = 0; i < seatedList.Count - 1; i++) {
            int left = seatedList[i];
            int right = seatedList[i + 1];
            int dist = (right - left) / 2;
            if (dist > maxDist) {
                maxDist = dist;
                bestSeat = left + dist;
            }
        }
        
        // Check last seat
        if (seatedList[seatedList.Count - 1] < n - 1) {
            int dist = n - 1 - seatedList[seatedList.Count - 1];
            if (dist > maxDist) {
                maxDist = dist;
                bestSeat = n - 1;
            }
        }
        
        seated.Add(bestSeat);
        return bestSeat;
    }
    
    public void Leave(int p) {
        seated.Remove(p);
    }
}
var ExamRoom = function(n) {
    this.n = n;
    this.seats = new Set();
};

ExamRoom.prototype.seat = function() {
    if (this.seats.size === 0) {
        this.seats.add(0);
        return 0;
    }
    
    let sortedSeats = Array.from(this.seats).sort((a, b) => a - b);
    let maxDist = 0;
    let bestSeat = 0;
    
    // Check seat 0
    if (!this.seats.has(0)) {
        maxDist = sortedSeats[0];
        bestSeat = 0;
    }
    
    // Check between seats
    for (let i = 0; i < sortedSeats.length - 1; i++) {
        let left = sortedSeats[i];
        let right = sortedSeats[i + 1];
        let mid = Math.floor((left + right) / 2);
        let dist = Math.min(mid - left, right - mid);
        
        if (dist > maxDist) {
            maxDist = dist;
            bestSeat = mid;
        }
    }
    
    // Check last seat
    if (!this.seats.has(this.n - 1)) {
        let dist = this.n - 1 - sortedSeats[sortedSeats.length - 1];
        if (dist > maxDist) {
            bestSeat = this.n - 1;
        }
    }
    
    this.seats.add(bestSeat);
    return bestSeat;
};

ExamRoom.prototype.leave = function(p) {
    this.seats.delete(p);
};

复杂度分析

操作时间复杂度空间复杂度
构造函数O(1)O(1)
seat()O(P)O(P)
leave()O(log P)O(P)

其中 P 表示当前已占用的座位数量。seat() 操作需要遍历所有已占用座位来寻找最佳位置,leave() 操作在有序集合中删除元素。空间复杂度主要用于存储已占用的座位。

相关题目