LeetCode855:考场就座
如题:
思路分析:
我们每次挑选座位的时候,需要里最近的人距离最大,也就是说如果要坐在两个人之间,应该坐在中点的位置
因此我们可以将两个座位抽象成一个线段,第一个人是线段开始位置start,第二个人是线段结束位置end,线段长度是end-start
此时我们就应该坐在线段的中点,离最近的人的距离就是线段长度
length/2
,因此我们要找出里最近的人的距离最大的位置=找出最大的那条线段此时就应该考虑能够动态找出最值的数据结构,一般是堆和平衡二叉树,但是堆只能操作堆顶元素,平衡二叉树可以操作根结点也可以操作孩子结点。由于我们不但需要添加座位,还需要随机离开某座位的时候删除该座位,因此只能操作最值的堆不可用,应该用平衡二叉树
java中操作平衡二叉树的数据结构有
TreeSet
和TreeMap
,这里使用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:考场就座相关推荐
- [Swift]LeetCode855. 考场就座 | Exam Room
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...
- leetcode855. 考场就座
题目地址 https://leetcode-cn.com/problems/exam-room/ 题目描述 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 . 当学生进入 ...
- leetcode-855. 考场就座
思路: 使用set(有序集合)记录seat的位置. 如果set为空,放入索引为0的位置. 遍历set,记录两个人之间距离的一半(pre+这个距离就是中点),如果这个距离比最大距离大,则记录pos和更新 ...
- LeetCode 855. 考场就座
855. 考场就座 [有序集合]用TreeSet存已经选好的座位,每次遍历所有的座位,如果两个之间的差值比当前max大,那么更新max和最后落座的位置.需要特别注意特判一下开头和结尾,因为有leave ...
- Leetcode 855. Exam Room 考场就座:提供两种解法
Leetcode 855. Exam Room 考场就座: 提供两种解法 855. Exam Room 考场就座(两种解法) 题目描述 示例: 解答1 代码1 解答2 代码2 855. Exam Ro ...
- 855. 考场就座(高频题)
855. 考场就座 题目 解题思路 代码 题目 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, -, N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座 ...
- LeetCode - OrderMap - 855.考场就座
题目 855.考场就座 难度 中等 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, -, N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上.如果有 ...
- LeetCode解析------855.考场就座
题目: 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, -, N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上.如果有多个这样的座位,他会坐在编号 ...
- 【855. 考场就座】
来源:力扣(LeetCode) 描述: 在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 . 当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上 ...
最新文章
- 了解一些多线程相关的知识
- word文档无法连接服务器,sql数据库无法连接服务器解决办法绝对有效
- Docker Review - docker 容器 常用命令
- ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
- 使用Couchbase分页
- 华为Mate50系列今年没戏:或明年第二季度末发布 5G有望回归
- linux xargs命令选项,使用xargs命令在Linux中执行多个操作 | MOS86
- ThreadPoolExecutor – Java线程池示例
- 计算机操作系统-1-总览
- 思科服务器中ftp的配置文件,思科服务器ftp的配置
- 计算机维护岗位主要职责怎么写,计算机维护岗位职责.doc
- Nginx+php+mysql超时问题总结
- 【上电即上华为云】华为云smart智联Cat.1+PLC无线网关_3121N-IED_MC615-CN-L610-CN
- 阿里系App抓包详细分析
- 常用的软件打包工具Inno Setup和AdvancedInstallerPortable
- java关于地图经纬度的计算
- 在真正的短信网络钓鱼攻击内部
- 诺基亚 java_诺基亚开放Symbian Javaapps了
- 对于BOT机器人的个人看法
- 6678开发板NDK网口通信完整实现(附源码)
热门文章
- AcWing 算法基础课第三节基础算法3 双指针、位运算、离散化、区间合并
- English语法_介词 - with
- Android画面显示流程分析(1)
- typescript-----javascript的超集,typescript学习笔记持续更新中......
- Windows下单机安装Spark开发环境
- pb_保存上次登陆的用户
- Mysql data export script [for PHP] update 2014/1/5
- Unity Color颜色转换 colorhex 转换color
- 全球与中国硅橡胶加热元件市场深度研究分析报告
- 关于在Ubuntu中修改Android源码的一些总结