来源:力扣(LeetCode)

描述:

在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1

当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上。如果有多个这样的座位,他会坐在编号最小的座位上。(另外,如果考场里没有人,那么学生就坐在 0 号座位上。)

返回 ExamRoom(int N) 类,它有两个公开的函数:其中,函数 ExamRoom.seat() 会返回一个 int (整型数据),代表学生坐的位置;函数 ExamRoom.leave(int p) 代表坐在座位 p 上的学生现在离开了考场。每次调用 ExamRoom.leave(p) 时都保证有学生坐在座位 p 上。

示例:

输入:["ExamRoom","seat","seat","seat","seat","leave","seat"], [[10],[],[],[],[],[4],[]]
输出:[null,0,9,4,2,null,5]
解释:
ExamRoom(10) -> null
seat() -> 0,没有人在考场里,那么学生坐在 0 号座位上。
seat() -> 9,学生最后坐在 9 号座位上。
seat() -> 4,学生最后坐在 4 号座位上。
seat() -> 2,学生最后坐在 2 号座位上。
leave(4) -> null
seat() -> 5,学生最后坐在 5 号座位上。

提示:

  • 1 <= N <= 109
  • 在所有的测试样例中 ExamRoom.seat() 和 ExamRoom.leave() 最多被调用 104 次。
  • 保证在调用 ExamRoom.leave§ 时有学生正坐在座位 p 上。

方法:延迟删除 + 有序集合 + 优先队列

  假设有两个学生,他们的位置分别为 s1 和 s2,我们用区间 [s1, s2] 表示他们之间的空闲座位区间。如果固定某一个区间,那么座位选择该区间的中点 s = s1 + ⌊(s2 − s1) / 2⌋ 能够使新进入的学生与离他最近的人之间的距离达到最大化。

  由题意可知,我们需要实时地维护这些区间的顺序关系,并且能实时地获取这些区间中最优区间(最优区间:能够使新进入的学生与离他最近的人之间的距离达到最大化),同时还要求实时地删除某个学生占用的座位以及修改对应的区间关系。现成的数据结构并不能很好地满足这些需求,我们尝试将删除区间这一操作延迟到获取最优区间时执行。

  使用有序集合 seats 保存已经有学生的座位编号,优先队列 pq 保存座位区间(假设优先队列中的两个区间分别为 [s1, s2] 和 [s3, s4],那么如果 ⌊(s2 − s1) / 2⌋ > ⌊(s4 − s3) / 2⌋或者 ⌊(s2 − s1) / 2⌋ = ⌊(s4 − s3) / 2⌋ and s1 < s2 ,那么区间 [s1, s2] 比区间 [s3 , s4] 更优)。

  • 对于 seat 函数:
    学生进入考场时,有三种情况:

    1. 考场没有一个学生,那么学生只能坐在座位 0;
      将座位 0 插入有序集合 seats,并且返回座位 0。
    2. 考场有超过两位学生,并且选择这些学生所在的座位组成的区间比直接坐在考场的最左或者最右的座位更优;
      首先判断优先队列中最优的区间是否有效(有效指当前区间的左右两个端点的座位有学生,中间的所有座位都没有学生),如果无效,删除该区间。设当前有效区间为 [s1, s2],最左的座位跟最左的有学生的座位的距离为 left,最右的座位跟最右的有学生的座位的距离为 right,如果 ⌊(s2 − s1) / 2⌋ > left 且 ⌊(s2 − s1) / 2⌋ ≥ right,那么选择当前最优区间比直接坐在考场的最左或者最右的座位更优,学生坐下的座位为 s = s1 + ⌊(s2 − s1) / 2⌋,将当前区间从优先队列 pq 中移除,然后分别将新增加的两个区间 [s1, s] 和 [s, s2] 插入优先队列 pq,将 s 插入有序集合 seats,返回座位 s。
    3. 考场少于两位学生,或者直接坐在考场的最左或者最右的座位比选择这些学生组成的区间更优。
      如果是最左的座位更优,那么将新增加的区间插入优先队列 pq,最左的座位插入有序集合 seats,并且返回最左的座位;最右的座位的做法类似。
  • 对于 leave 函数:
    如果要删除的座位 p 上的学生不是所有学生的最左或者最右的学生,那么删除该学生会产生新的区间,我们将该区间放入优先队列 pq 中,然后在有序集合 seats 中删除该学生;否则只需要在有序集合 seats 中删除该学生。对于删除座位后已经无效的区间,我们只需要在 seat 函数中判断区间是否有效即可。

代码:

struct Comp {bool operator()(const pair<int, int> &p1, const pair<int, int> &p2) {int d1 = p1.second - p1.first, d2 = p2.second - p2.first;return d1 / 2 < d2 / 2 || (d1 / 2 == d2 / 2 && p1.first > p2.first);}
};class ExamRoom {private:int n;set<int> seats;priority_queue<pair<int, int>, vector<pair<int, int>>, Comp> pq;public:ExamRoom(int n) : n(n) {}int seat() {if (seats.empty()) {seats.insert(0);return 0;}int left = *seats.begin(), right = n - 1 - *seats.rbegin();while (seats.size() >= 2) {auto p = pq.top();if (seats.count(p.first) > 0 && seats.count(p.second) > 0 && *next(seats.find(p.first)) == p.second) { // 不属于延迟删除的区间int d = p.second - p.first;if (d / 2 < right || d / 2 <= left) { // 最左或最右的座位更优break;}pq.pop();pq.push({p.first, p.first + d / 2});pq.push({p.first + d / 2, p.second});seats.insert(p.first + d / 2);return p.first + d / 2;}pq.pop(); // leave 函数中延迟删除的区间在此时删除}if (right > left) { // 最右的位置更优pq.push({*seats.rbegin(), n - 1});seats.insert(n - 1);return n - 1;} else {pq.push({0, *seats.begin()});seats.insert(0);return 0;}}void leave(int p) {if (p != *seats.begin() && p != *seats.rbegin()) {auto it = seats.find(p);pq.push({*prev(it), *next(it)});}seats.erase(p);}
};

执行用时:32 ms, 在所有 C++ 提交中击败了99.35%的用户
内存消耗:20.5 MB, 在所有 C++ 提交中击败了16.56%的用户
复杂度分析
时间复杂度: seat 函数:均摊时间复杂度 O(logm),其中 m 是调用 seat 函数的次数。因为优先队列最多保存不超过 2×m 个元素,所以一次 seat 函数平均只有不超过 2 次的优先队列延迟删除操作,对优先队列和有序集合操作的时间复杂度都是 O(logm)。
leave 函数:O(logm)。删除有序集合 seats 的一个元素和优先队列插入一个元素的时间复杂度都是 O(logm)。
空间复杂度: O(m)。有序集合 seats 和优先队列 pq 中最多保存不超过 2×m 个元素。
author:LeetCode-Solution

【855. 考场就座】相关推荐

  1. LeetCode 855. 考场就座

    855. 考场就座 [有序集合]用TreeSet存已经选好的座位,每次遍历所有的座位,如果两个之间的差值比当前max大,那么更新max和最后落座的位置.需要特别注意特判一下开头和结尾,因为有leave ...

  2. 855. 考场就座(高频题)

    855. 考场就座 题目 解题思路 代码 题目 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, -, N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座 ...

  3. LeetCode - OrderMap - 855.考场就座

    题目 855.考场就座 难度 中等 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, -, N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上.如果有 ...

  4. LeetCode解析------855.考场就座

    题目: 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, -, N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上.如果有多个这样的座位,他会坐在编号 ...

  5. Leetcode 855. Exam Room 考场就座:提供两种解法

    Leetcode 855. Exam Room 考场就座: 提供两种解法 855. Exam Room 考场就座(两种解法) 题目描述 示例: 解答1 代码1 解答2 代码2 855. Exam Ro ...

  6. Leetcode 855:考场就座

    题目描述 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上.如果有多个这样的座位,他会坐 ...

  7. JAVA程序设计:考场就座(LeetCode:855)

    在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上.如果有多个这样的座位,他会坐在编号最小 ...

  8. 【LeetCode - 855】考场就座

    文章目录 1.题目描述 2.解题思路 3.解题代码 1.题目描述 2.解题思路 初始化:定义一个 TreeSet 存储已经占有的座位号,该数据结构会自动从小到大排序: seat: 1)定义一个全局最优 ...

  9. [Swift]LeetCode855. 考场就座 | Exam Room

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...

最新文章

  1. xp创建虚拟服务器,Xp系统怎么创建虚拟目录?Xp系统创建虚拟目录的方法
  2. 深入XP之认识的引导文件NTLDR
  3. (转)java类初始化顺序 - jackyrong的世界 - 博客园
  4. 华为云ModelArts2.0来袭
  5. MySQL5.7创建数据库与添加用户、删除用户及授权、保证数据库账号安全
  6. 关于chrome上的网银安全控件开发技术(chrome 调用本地dll)
  7. java 分布式日志_打造分布式日志收集系统
  8. 如何面对科技性失业?
  9. 如何在Mac上裁剪图片,教你几个技巧
  10. WDS桥接副路由器有线上网方法
  11. python 字符串转字典的几种方法
  12. 用计算机写作文的好处,谈谈对电脑写作的意义,电脑写作与网络写作的含
  13. opencv_dnn模型部署学习记录
  14. php 自带加密、解密函数
  15. 徐雷FrankXuLei受邀为阿里巴巴集团马云老板的恒生集团讲授《分布式高并发HA架构和全新.net core高级课程》...
  16. 求小组各科的平均分和总平均分
  17. 计算机网络原理自考常考,计算机网络原理自考_大题__归类复习剖析
  18. Java构造函数(构造方法)的简介说明
  19. Modbus Tcp协议详解
  20. “松果”团队分拆出“大鱼” 小米芯片业务扩张加速

热门文章

  1. 设置按钮为点击状态(被点击)
  2. python判断手机号码是否正确_Python程序验证输入的电话号码是否正确
  3. 【整理】Word OpenXML常用标签
  4. 抢食!传google将依托自有光纤推无线网络效劳
  5. JVM:(一)JVM与Java体系结构
  6. 企业应采用可收回金额与账面价值孰低的方法进行减值测试的有
  7. 基于S3C2440的linux-3.6.6移植——LED驱动
  8. 做技术要有一颗平常心
  9. 音乐识别科技公司SHAZAM十年来终于再次盈利
  10. java的简单网络爬虫(爬取花瓣网的图片)