You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

226 lines
7.0 KiB

2 years ago
package class_2022_11_3_week;
import java.util.HashSet;
import java.util.TreeSet;
// 设计一个叫Bank的类并提供如下方法
// Bank(int n) 初始化的时候准备好0、1、2 ... n-1个座位
// int hello() :
// 如果此时所有座位都无人那么分配0号座位给当前用户
// 如果此时座位上有人,那么分配一个座位,这个座位保证是所有座位中离最近的人距离最远的座位
// 如果有多个座位都满足,分配座位编号最小的座位
// 返回座位编号
// 如果已经没有座位,返回-1表示无法分配
// void goodbye(int x) :
// 如果x号座位上无人什么也不用做
// 如果x号座位上有人现在这个人离开了该座位又能重新考虑分配
// 举例 :
// Bank b = new Bank(10) 0~9号座位被初始化出来
// b.hello()返回0表示给当前用户分配了0座位
// b.hello()返回9因为此时9座位离0座位的人最远此时
// 0 1 2 3 4 5 6 7 8 9
// X X
// b.hello()虽然座位4和座位5离最近人的距离都是4(最远)
// 这种情况根据描述分配座位编号最小的座位返回4此时
// 0 1 2 3 4 5 6 7 8 9
// X X X
// b.hello()座位2、座位6、座位7都是离最近人的距离最远的(2)
// 这种情况根据描述分配座位编号最小的座位返回2此时
// 0 1 2 3 4 5 6 7 8 9
// X X X X
// b.goodbye(4)4座位的人离开了此时
// 0 1 2 3 4 5 6 7 8 9
// X X X
// b.hello()座位5、座位6都是离最近人的距离最远的(3)
// 这种情况根据描述分配座位编号最小的座位返回5
// 测试连接 : https://leetcode.cn/problems/exam-room/
public class Code05_FarAwaySuggestion {
class ExamRoom {
// 空闲座位的类
// 比如一共8个空间
// 0 1 2 3 4 5 6 7
// 如果某个空闲座位区间是0~3
// 0 1 2 3 X
// 0是开头3是结尾提供4的距离因为0位置离X位置最远
// 如果某个空闲座位区间是4~7
// X 4 5 6 7
// 4是开头7是结尾提供4的距离因为7位置离X位置最远
// 如果某个空闲座位区间是2~5
// X 2 3 4 5 X
// 2是开头5是结尾提供2的距离因为3、4位置离X位置最远
// 根据start、end的具体情况可以算出far也可以算出该分配那个座位
public static class FreeSpace {
public int start;
public int end;
public int far;
public FreeSpace(int a, int b, int c) {
start = a;
end = b;
far = c;
}
}
// right是最右的座位在哪
public int right;
// 所有空闲区间都在seats里
// far越远越早使用
// far一样start越小越早使用
// 需要看体系学习班,有序表、比较器的内容
public TreeSet<FreeSpace> seats;
// 所有空闲区间,根据开头位置从小到大的一个有序表
// 方便定位,举个例子
// 比如所有的空闲区间为:
// 3~7
// 8~10
// 23~56
// 64~78
// 在heads中这些空闲区间是根据开头位置组织的
// 假设x = 60位置然后被释放了
// 你需要找到 <= 60位置最近的空闲区间
// 你需要找到 >= 60位置最近的空闲区间
// 因为你需要可能需要把x位置左边、x位置、x位置右边
// 三段合起来如果x可以联通的话
// 那么你就可以用这个有序表,快速定位
// 查找 <= 60且最近的区间就是有序表的floor方法速度很快
// 查找 >= 60且最近的区间就是有序表的ceiling方法速度很快
// 然后你就能查到:
// 23~56 60~60 64~78
// 不能合并因为这三段无法通过60~60连起来
// 但是如果能连接的话,这么做就很方便了
public TreeSet<FreeSpace> heads;
// 那些座位已经使用了,都在这个哈希表里
public HashSet<Integer> used;
public ExamRoom(int n) {
right = n - 1;
// far越远越早使用
// far一样start越小越早使用
// 看比较器的内容!
seats = new TreeSet<>((a, b) -> a.far != b.far ? (b.far - a.far) : (a.start - b.start));
// 根据开头位置从小到大的一个有序表
heads = new TreeSet<>((a, b) -> a.start - b.start);
used = new HashSet<>();
// 最开始时0~n-1整个范围都是空闲区间
add(0, right, Integer.MAX_VALUE);
}
public int seat() {
if (used.size() == right + 1) {
return -1;
}
FreeSpace cur = poll();
int ans;
// 比如有8个位置
// 0 1 2 3 4 5 6 7
// 如果cur.start == 0, cur.end == 7
// 此时肯定使用0位置
if (cur.start == 0 && cur.end == right) {
ans = 0;
add(1, right, right);
} else if (cur.start == 0) {
// 如果cur.start == 0, cur.end != 7
// 0(s) 1 2 3 4 5(e) 6 7
// 此时肯定使用0位置
// 剩下就是1(s)...e
// 提供的距离就是(e - s) / 2 + 1
ans = 0;
int start = 1;
int end = cur.end;
if (start <= end) {
add(start, end, (end - start) / 2 + 1);
}
} else if (cur.end == right) {
// 如果cur.start != 0, cur.end == 7
// 0 1 2(s) 3 4 5 6 7(e)
// 此时肯定使用7位置
// 剩下就是s...6(e)
// 提供的距离就是(e - s) / 2 + 1
ans = right;
int start = cur.start;
int end = cur.end - 1;
if (start <= end) {
add(start, end, (end - start) / 2 + 1);
}
} else {
// 如果cur.start != 0, cur.end != 7
// 0 1 2(s) 3 4 5 6(e) 7
// 此时肯定使用中点位置,(s + e) / 2
// 0 1 2(s) 3 4(中点) 5 6(e) 7
// 剩下就是s...中点-1
// 还剩下,中点+1...e
ans = (cur.start + cur.end) / 2;
int start1 = cur.start;
int end1 = ans - 1;
int start2 = ans + 1;
int end2 = cur.end;
if (start1 <= end1) {
add(start1, end1, (end1 - start1) / 2 + 1);
}
if (start2 <= end2) {
add(start2, end2, (end2 - start2) / 2 + 1);
}
}
used.add(ans);
return ans;
}
private void add(int start, int end, int distance) {
FreeSpace space = new FreeSpace(start, end, distance);
seats.add(space);
heads.add(space);
}
private FreeSpace poll() {
FreeSpace space = seats.pollFirst();
heads.remove(space);
return space;
}
public void leave(int x) {
if (used.contains(x)) {
used.remove(x);
FreeSpace m = new FreeSpace(x, x, 1);
FreeSpace l = heads.floor(m);
FreeSpace r = heads.ceiling(m);
merge(l, m, r);
}
}
// 左区间、中区间、右区间
// 能合并就合在一起
private void merge(FreeSpace l, FreeSpace m, FreeSpace r) {
int start = m.start;
int end = m.end;
if (l != null && l.end == m.start - 1) {
remove(l);
start = l.start;
}
if (r != null && m.end + 1 == r.start) {
remove(r);
end = r.end;
}
int far = 0;
if (start == 0 && end == right) {
far = Integer.MAX_VALUE;
} else if (start == 0) {
far = end + 1;
} else if (end == right) {
far = end - start + 1;
} else {
far = (end - start) / 2 + 1;
}
add(start, end, far);
}
private void remove(FreeSpace space) {
seats.remove(space);
heads.remove(space);
}
}
}