23-05-19 刷题
23-05-19 刷题
非常难的一个题目,然后解法也非常多。
【Hard】218. 天际线问题 - 力扣(LeetCode)
非常难的题目,需要多复习,和深入理解。
实现1:使用扫描线(Swipeline + PriorityQueue来实现)
class Solution {
public List<List<Integer>> getSkyline(int[][] buildings) {
List<List<Integer>> ans = new ArrayList<>();
// preprocess: using sweepline
// 预处理扫描线 e[i][0]: x, e[i][1]: height (with flag)
List<int[]> points = new ArrayList<>();
for(int[] building: buildings) {
points.add(new int[] {building[0], -building[2]});
points.add(new int[] {building[1], building[2]});
}
// sort the sweep points from left to right in x-axis, and then sort by the height
// if x in same, for left edge of building: highest first (neg hight)
// for right edge of building: lowest first (pos hight) think why?
Collections.sort(points, (a, b) -> {
if (a[0] != b[0]) return Integer.compare(a[0], b[0]);
return Integer.compare(a[1], b[1]);
});
// core handling logic
int pre = 0;
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> Integer.compare(b, a));
maxHeap.offer(0);
for(int[] point: points) {
if (point[1] < 0) { // left edge
maxHeap.offer(-point[1]);
} else {
maxHeap.remove(point[1]);
}
int cur = maxHeap.peek();
if (cur != pre) {
ans.add(Arrays.asList(point[0], cur));
pre = cur;
}
}
return ans;
}
}
复杂度分析:
- 时间:O(nlogn) 预处理扫描线,排序O(nlogn),后面核心处理逻辑,总共有n个高度,各加入优先队列和删除一次,优先队列的操作时间复杂度是O(log n),所以总共的时间复杂度是:O(n logn)
- 空间:O(n)
实现2:使用扫描线算法(Swipeline + MultiSet来实现)
public List<List<Integer>> getSkyline(int[][] buildings) {
List<List<Integer>> ans = new ArrayList<>();
// preprocess: using sweepline
List<int[]> points = new ArrayList<>();
for(int[] building: buildings) {
points.add(new int[] {building[0], -building[2]});
points.add(new int[] {building[1], building[2]});
}
// sort the sweep points from left to right in x-axis, and then sort by the height
// if x in same, for left edge of building: highest first (neg hight)
// for right edge of building: lowest first (pos hight) think why?
Collections.sort(points, (a, b) -> {
if (a[0] != b[0]) return Integer.compare(a[0], b[0]);
return Integer.compare(a[1], b[1]);
});
// core handling logic
int pre = 0;
// key: height, value: count
TreeMap<Integer, Integer> multiSet = new TreeMap<>();
multiSet.put(0, 1); // don't understand why use it here
for(int[] point: points) {
if (point[1] < 0) { // left edge
multiSet.put(-point[1], multiSet.getOrDefault(-point[1], 0) + 1);
} else { // right edge, remove one height
if (multiSet.get(point[1]) == 1) {
multiSet.remove(point[1]);
} else {
multiSet.put(point[1], multiSet.get(point[1]) - 1);
}
}
int cur = multiSet.lastKey(); // current max height
if (cur != pre) { // there is a hight difference, so it must have a key point
ans.add(Arrays.asList(point[0], cur));
pre = cur;
}
}
return ans;
}
这个题的最佳解法,时间复杂度是O(n).空间是:O(n)
参考视频:
复习并查集的写法
上面这个题有一种解法是使用并查集,时间复杂度也是O(n logn)。
使用并查集时,可以直接定义这样的类(数据结构)。直接拿来用。
// Define the disjoint-set structure.
class UnionFind {
int[] parent;
public UnionFind(int n) {
this.parent = new int[n];
for (int i = 0; i < n; ++i)
parent[i] = i;
}
public int find(int x) {
return parent[x] == x ? x : (parent[x] = find(parent[x]));
}
public void union(int x, int y) {
int u = find(x), v = find(y);
if (u != v) { // x & y are in different set, so add u to v set
parent[u] = v;
}
}
}
别人写的版本:
// Define the disjoint-set structure.
class UnionFind {
int[] root;
public UnionFind(int n) {
this.root = new int[n];
for (int i = 0; i < n; ++i)
root[i] = i;
}
public int find(int x) {
return root[x] == x ? x : (root[x] = find(root[x]));
}
public void union(int x, int y) {
root[x] = root[y];
}
}