• 如题

  • 思路分析:

    • 我们每次挑选座位的时候,需要里最近的人距离最大,也就是说如果要坐在两个人之间,应该坐在中点的位置

    • 因此我们可以将两个座位抽象成一个线段,第一个人是线段开始位置start,第二个人是线段结束位置end,线段长度是end-start

    • 此时我们就应该坐在线段的中点,离最近的人的距离就是线段长度length/2,因此我们要找出里最近的人的距离最大的位置=找出最大的那条线段

    • 此时就应该考虑能够动态找出最值的数据结构,一般是堆和平衡二叉树,但是堆只能操作堆顶元素,平衡二叉树可以操作根结点也可以操作孩子结点。由于我们不但需要添加座位,还需要随机离开某座位的时候删除该座位,因此只能操作最值的堆不可用,应该用平衡二叉树

    • java中操作平衡二叉树的数据结构有TreeSetTreeMap,这里使用TreeSet

    • 由于我们存放的是线段,这里可以用一个专门的内部类来表示,属性就包含线段开始位置start线段结束位置end,和线段长度end-start

      • 注意:两个座位形成的线段之间不能有其他座位
      static class Site {private int start;private int end;private int length;public Site(int start, int end, int length) {this.start = start;this.end = end;this.length = length;}
      }
      

      也可以使用数组表示,但是可读性没那么好

  • 代码

    class ExamRoom {// 因为需要用leave()删除座位,例如此时座位是[1,3,5],我们要删除座位3,那么[1,3]和[3,5]应该合成[1,5],// 此时需要根据座位号3找出[1,3]和[3,5],然后在平衡二叉树中删除它,左边的线段以3为end,右边的线段以3为start// 因此我们用两个hashMap分别保存以3为start的线段和以3为end的线段private Map<Integer, Site> startMap;private Map<Integer, Site> endMap;//平衡二叉树private TreeSet<Site> tree;//将构造器中的n作为属性,供其他方法使用private int N;public ExamRoom(int n) {N = n;startMap = new HashMap<>();endMap = new HashMap<>();//我们每次需要找一个新座位的时候,就是找出一条长度最大的线段,然后坐在该线段中间length/2的位置,因此我们比较器直接比较length/2的大小//注意:0和N两个最左和最右座位,因为最开始选的时候必须选0,此时还无法构成线段,因此考虑加入两个哨兵座位-1和N,他们构成初始线段[-1,N],//而我们实际座位应该在[0,N-1]之间//注意:普通情况下我们比较器比较两个元素的length/2的大小,因为我们选了这条线段就必须坐中间length/2的位置,保证离两端最远,但是// 如果有一个元素的开始或结束位置是哨兵位置,那么我们选该线段就不用坐中间,我们坐哨兵位置边上,因为哨兵不是座位,因此此时比较的是length-1(除开哨兵)tree = new TreeSet<>((a, b) -> {int lengthA = 0;int lengthB = 0;//如果a包含哨兵if (a.start == -1 || a.end == N) lengthA = a.length - 1;else lengthA = a.length / 2;//如果b包含哨兵if (b.start == -1 || b.end == N) lengthB = b.length - 1;else lengthB = b.length / 2;//如果长度一样就按起始位置start从小到大排序if (lengthA == lengthB)return a.start - b.start;//否则按长度从大到小排序return lengthB - lengthA;});//初试线段,起始结束位置都是哨兵,线段长度是end-startSite start = new Site(-1, N, N + 1);//初始化tree和两个maptree.add(start);startMap.put(start.start, start);endMap.put(start.end, start);}public int seat() {//选座位的时候挑出最大线段,坐中间,此时产生会两个新的线段[start,mid],[mid,end],记录下来Site site1 = null;Site site2 = null;//挑出最大线段Site site = tree.pollFirst();//上面提到过,如果线段包含哨兵,那么就不是坐中间,而是做哨兵边上if (site.start == -1) {//start是哨兵site1 = new Site(-1, 0, 1);site2 = new Site(0, site.end, site.end);}//end是哨兵else if (site.end == N) {site1 = new Site(site.start, N - 1, (N - 1) - site.start);site2 = new Site(N - 1, N, 1);}//都不是哨兵,坐中间else {site1 = new Site(site.start, site.start + site.length / 2, site.length / 2);site2 = new Site(site.start + site.length / 2, site.end, site.end - site.start - site.length / 2);}//将以该线段从两个hashMap中移出startMap.remove(site.start);endMap.remove(site.end);//在平衡二叉树中加入新生成的两个线段tree.add(site1);tree.add(site2);//在两个hashMap中加入新生成的两个线段startMap.put(site1.start, site1);endMap.put(site1.end, site1);startMap.put(site2.start, site2);endMap.put(site2.end, site2);//返回新挑选的座位号,site1.end=site2.start,返回哪一个都一样return site1.end;}//离开座位的时候应该将该座位左右两端的线段合并public void leave(int p) {//先从两个hashMap中移出以该座位号为开始的线段和以该座位为结束的线段,然后得到这两个线段,在tree中也删除这两个线段Site site0 = startMap.remove(p);Site site1 = endMap.remove(p);tree.remove(site0);tree.remove(site1);//将两个线段合并,生成新线段,然后加入tree和两个hashMap中Site site = new Site(site1.start, site0.end, site0.end - site1.start);tree.add(site);startMap.put(site.start,site);endMap.put(site.end,site);}static class Site {private int start;private int end;private int length;public Site(int start, int end, int length) {this.start = start;this.end = end;this.length = length;}}
    }
    
  • 注意事项:我们TreeSet中的比较器比较的是length/2,不能比较length

    • 因为如果有线段[0,4][5,10],那么比较长度的话[5,10]长度是5,[0,4]长度是4,我们会选择[5,10],但是5是奇数,5/2=2,我们和最近的人距离是2。
    • [0,4]的长度4是偶数,4/2=2,我们和最近的人的距离也是2。
    • 根据题意,距离一样我们应该选靠前的座位,因此此时应该选[0,4],选[0,5]就错了

LeetCode855:考场就座相关推荐

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

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

  2. leetcode855. 考场就座

    题目地址 https://leetcode-cn.com/problems/exam-room/ 题目描述 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 . 当学生进入 ...

  3. leetcode-855. 考场就座

    思路: 使用set(有序集合)记录seat的位置. 如果set为空,放入索引为0的位置. 遍历set,记录两个人之间距离的一半(pre+这个距离就是中点),如果这个距离比最大距离大,则记录pos和更新 ...

  4. LeetCode 855. 考场就座

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

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

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

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

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

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

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

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

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

  9. 【855. 考场就座】

    来源:力扣(LeetCode) 描述: 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上 ...

最新文章

  1. 移动端调取摄像头上面如何给出框_飞桨实战笔记:自编写模型如何在服务器和移动端部署...
  2. 62岁程序员植入逻辑炸弹, 面临10年监禁和25万美元罚款
  3. 5分钟实现动态切换,原来迁徙图还可以这么玩!
  4. 把List对象或者单值对象转换为Json格式
  5. LeetCode 931. 下降路径最小和(DP)
  6. delphi7 获取dll的类_跟我学Java内存管理----JMM精华终章(类加载器)
  7. 郁金香汇编代码注入怎么写看雪_世界黑客编程大赛冠军的汇编代码 你见过吗?...
  8. MySQL 备份与恢复
  9. oracle预备份,oracle自动备份
  10. Android开发中的单元测试
  11. 在Android关机中插入脚本
  12. QLV格式的视频怎么在线转化成MP4
  13. js知识梳理1:理解对象的属性特性
  14. 小米路由器(R2D)开发版-固件分析
  15. SQLServer2012 查询分析器的快捷键
  16. js柯里化的认识(本文转载自https://www.zhangxinxu.com/wordpress/2013/02/js-currying),觉得很有用就记下了
  17. 2017ACM-ICPC亚洲区域赛(西安站)
  18. verilog的免费编译器
  19. 永远等你先挂电话[ZZ]
  20. 八款好用的浏览器兼容性测试工具推荐

热门文章

  1. 网页两侧flash广告代码
  2. Stanford-CV华人教授李飞飞写给她学生的一封信,如何做好研究以及写好PAPER,受益匪浅
  3. Kudu-Java-api操作表(DDL)
  4. 安全认证Kerberos的安装与操作
  5. 数据库数据 | TCGA数据库33种癌症的 miRNA Isoform Expression数据
  6. 三月随笔——漫长的等待
  7. 工业互联网和边缘计算学习总结
  8. android区域和gynoid区域,甲状腺功能正常的2型糖尿病患者甲状腺激素与体脂分布的相关性研究-内科学(内分泌)专业论文.docx...
  9. 天津天地伟业程序员怎么样_关注|天津217家融资公司“失联”!发现快举报
  10. img图片加载前显示load图片