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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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);
}
}
}