考场就座(medium)
做题过程
真的不会,想不到啊。
算法概述
原题
本题要求为设计一个类来管理考场座位,使新加入的学生的座位总是远离别人(达到最大距离)。需要使用 优先队列 和 有序集合 来管理座位区间,进而对距离进行计算,还需要使用 延迟删除 同步在删除学生的同时更新队列。
- 时间复杂度为O(logm):都是对数据结构(有序集合和优先队列/堆的操作)
- 空间复杂度为O(m):一个学生一个区间,加上删除操作,小于等于2*m个数字要保存
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| class ExamRoom { int n; TreeSet<Integer> seats; PriorityQueue<int[]> pq;
public ExamRoom(int n) { this.n=n; this.seats=new TreeSet<Integer>(); this.pq=new PriorityQueue<int[]>((a,b)->{ int d1=a[1]-a[0],d2=b[1]-b[0];
return d1/2<d2/2||(d1/2==d2/2&&a[0]>b[0])?1:-1; }); } public int seat() { if(seats.isEmpty()){ seats.add(0); return 0; }
int left=seats.first(),right=n-1-seats.last(); while(seats.size()>=2){ int[] p=pq.peek(); if(seats.contains(p[0])&&seats.contains(p[1])&&seats.higher(p[0])==p[1]){ int d=p[1]-p[0]; if(d/2<right||d/2<=left) break; pq.poll(); pq.offer(new int[]{p[0],p[0]+d/2}); pq.offer(new int[]{p[0]+d/2,p[1]}); seats.add(p[0]+d/2); return p[0]+d/2; } pq.poll(); } if(right>left){ pq.offer(new int[]{seats.last(),n-1}); seats.add(n-1); return n-1; }else{ pq.offer(new int[]{0,seats.first()}); seats.add(0); return 0; } } public void leave(int p) { if(p!=seats.first()&&p!=seats.last()){ int prev=seats.lower(p),next=seats.higher(p); pq.offer(new int[]{prev,next}); } seats.remove(p); } }
|
重要实例方法及属性(JAVA)
TreeSet.lower()
和TreeSet.higher()
:和ceiling
以及floor()
不同的是,后两者为 非严格 ,前两者是 严格 。
总结
这道题利用了多个数据结构的特性以及技巧。
- 有序集合:实时得到 最小值和最大值
- 优先队列:保存区间元素,使用 灵活的自定义比较器
- 延迟删除:在找到前驱和后继组成新的区间(i.e., 图论的收缩边)后再删除该节点
好难啊😐