原题描述很长,此处放链接:https://leetcode.com/problems/the-skyline-problem/description/
思路:
题目要求所有建筑行程的轮廓,并且给出高度变化的点即可。
暴力的做法,求出所有建筑的左右边界,遍历这个范围,在每个点上找出当前的最高值,如果与前一个最高值相同就跳过。
优化的解法是参考其他网上的解答,下面是自己做完后的思考。
上面的算法有两个问题:
- 有些点不需要查找高度,因为它不是有建筑物高度变化的地方。
- 在有建筑物高度变化的地方,遍历了所有建筑物,如果有一个数据结构能够直接求出当前的最大高度,那么时间复杂度就变小了,而这个数据结构就是堆。
第一步,我们用一个list来存储所有建筑物高度发生变化的点,要记录坐标和高度,然后先按坐标,再按高度排序,为了后续对堆的处理,这里需要规定建筑起点的高度记录为负数,终点记录为正数。
第二步,初始化一个最大堆(堆顶始终保持着当前堆内最大值),我们遍历上面的list,对每个元素,如果导致最大高度发生变化,那么这是一个轮廓改变点,需要加到结果集中。具体讲,对每个元素,如果是起点,需要把这个高度加入到堆内,把对之前做的负值再取负;如果是终点,证明这个建筑要在此消失了,需要把这个高度从堆内删除。做完上面的操作后,比较之前记录的轮廓高度与当前堆顶记录的最高高度,如果不同,就证明这个轮廓的高度发生变化了。
上面有一个操作是要从堆内删除元素,用JAVA的PriorityQueue,时间复杂度是O(n),这里可以用TreeMap或TreeSet进行优化。
public List<int[]> getSkyline(int[][] buildings) {
List<int[]> res = new ArrayList<>();
if (buildings == null || buildings.length == 0 || buildings[0].length == 0) {
return res;
}
//init height list
List<int[]> Coors = new ArrayList<>();
for (int[] building : buildings) {
Coors.add(new int[]{building[0], -building[2]});
Coors.add(new int[]{building[1], building[2]});
}
Collections.sort(Coors, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[0] != o2[0]) {
return o1[0] - o2[0];
}
return o1[1] - o2[1];
}
});
//init PriorityQueue, keep the max height until now
Queue<Integer> maxHeightHeap = new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer a, Integer b) {
return b - a;
}
});
maxHeightHeap.add(0);
int pre = 0, cur = 0;
for (int i = 0; i < Coors.size(); i++) {
int[] Coor = Coors.get(i);
if (Coor[1] < 0) {
maxHeightHeap.add(-Coor[1]);
} else {
maxHeightHeap.remove(Coor[1]);
}
cur = maxHeightHeap.peek();
if (cur != pre) {
res.add(new int[]{Coor[0], cur});
pre = cur;
}
}
return res;
}