LeetCode题218——The Skyline Problem

https://leetcode.com/problems/the-skyline-problem/description/

A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Now suppose you are given the locations and height of all the buildings as shown on a cityscape photo (Figure A), write a program to output the skyline formed by these buildings collectively (Figure B).

 

The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX0 < Hi ≤ INT_MAX, and Ri - Li > 0. You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0.

For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] .

The output is a list of "key points" (red dots in Figure B) in the format of [ [x1,y1], [x2, y2], [x3, y3], ... ] that uniquely defines a skyline. A key point is the left endpoint of a horizontal line segment. Note that the last key point, where the rightmost building ends, is merely used to mark the termination of the skyline, and always has zero height. Also, the ground in between any two adjacent buildings should be considered part of the skyline contour.

For instance, the skyline in Figure B should be represented as:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ].

Notes:

  • The number of buildings in any input list is guaranteed to be in the range [0, 10000].
  • The input list is already sorted in ascending order by the left x position Li.
  • The output list must be sorted by the x position.
  • There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...[2 3], [4 5], [7 5], [11 5], [12 7]...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...[2 3], [4 5], [12 7], ...]

思路

比较经典的一题,题目太长完全不需要全部看,直接看两张图以及输入输出就知道题目要干啥了。

看了解析https://briangordon.github.io/2014/08/the-skyline-problem.html,从矩形的左右边入手,将左边和右边在x轴上的点作为critical point,然后:

for each critical point c
    c.y gets the height of the tallest rectangle over c

要做的是确定cirtical point的高度。在遍历这些critical point时,通过一个堆来得知 over current critical point c的所有矩形的最高高度,更新critical point的高度。堆顶是最高的点,如果没被移出则表明这个最高的矩形还没结束。对于矩形的左顶点,将其加入堆中,对于右顶点,找出堆中其相应的左顶点并移除它,也就是说在我们从左往右遍历critical point时这个矩形已经结束了。具体代码中,为了在排序后的顶点列表中区分左右顶点,左顶点的值是负数,而右顶点值则存的是正数。

堆中先加入一个零点高度,帮助我们在只有最矮的建筑物时选择最低值。

int[][] buildings, 矩形被存储了一个二维数组,每一行是一个矩形,有3个值,分别是矩形的底在x轴上的左边的点和右边的点,以及矩形的高度,比如[2 9 10]
int[] height, 用于记录critical point所在边的高度,只有两个值,分别是x轴坐标和高度,比如height[0]=1,height[1]=4,表示在x轴为1处的边其高为4
public class Solution {
    public List<int[]> getSkyline(int[][] buildings) {
        List<int[]> result = new ArrayList<>();
        List<int[]> height = new ArrayList<>();
        // 拆解矩形,构建顶点的列表
        for(int[] b:buildings) {
            // 左顶点存为负数
            height.add(new int[]{b[0], -b[2]});
            // 右顶点存为正数
            height.add(new int[]{b[1], b[2]});
        }
        // 根据横坐标对列表排序,相同横坐标的点纵坐标小的排在前面
        Collections.sort(height, new Comparator<int[]>(){
            public int compare(int[] a, int[] b){
                if(a[0] != b[0]){
                    return a[0] - b[0];
                } else {
                    return a[1] - b[1];
                }
            }
        });
        // 构建堆,按照纵坐标来判断大小
        Queue<Integer> pq = new PriorityQueue<Integer>(new Comparator<Integer>(){
            public int compare(Integer i1, Integer i2){
                return i2 - i1;
            }
        });
        // 将地平线值0先加入堆中
        pq.offer(0);
        // prev用于记录上次keypoint的高度
        int prev = 0;
        for(int[] h:height) {
            // 将左顶点加入堆中
            if(h[1] < 0) {
                pq.offer(-h[1]);
            } else {
            // 将右顶点对应的左顶点移去
                pq.remove(h[1]);
            }
            int cur = pq.peek();
            // 如果堆的新顶部和上个keypoint高度不一样,则加入一个新的keypoint
            if(prev != cur) {
                result.add(new int[]{h[0], cur});
                prev = cur;
            }
        }
        return result;
    }
}

参考:https://segmentfault.com/a/1190000003786782

注意上面比较器和用PriorityQueue来实现堆数据结构,比较器在java 1.8中还可以通过Lambda表达式来实现,简洁很多。

猜你喜欢

转载自www.cnblogs.com/f91og/p/9465854.html