218. 天际线问题 (JAVA)

城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。现在,假设您获得了城市风光照片(图A)上显示的所有建筑物的位置和高度,请编写一个程序以输出由这些建筑物形成的天际线(图B)。

 

每个建筑物的几何信息用三元组 [Li,Ri,Hi] 表示,其中 Li 和 Ri 分别是第 i 座建筑物左右边缘的 x 坐标,Hi 是其高度。可以保证 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX 和 Ri - Li > 0。您可以假设所有建筑物都是在绝对平坦且高度为 0 的表面上的完美矩形。

例如,图A中所有建筑物的尺寸记录为:[ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] 。

输出是以 [ [x1,y1], [x2, y2], [x3, y3], ... ] 格式的“关键点”(图B中的红点)的列表,它们唯一地定义了天际线。关键点是水平线段的左端点。请注意,最右侧建筑物的最后一个关键点仅用于标记天际线的终点,并始终为零高度。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。

例如,图B中的天际线应该表示为:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]。

说明:

任何输入列表中的建筑物数量保证在 [0, 10000] 范围内。
输入列表已经按左 x 坐标 Li  进行升序排列。
输出列表必须按 x 位排序。
输出天际线中不得有连续的相同高度的水平线。例如 [...[2 3], [4 5], [7 5], [11 5], [12 7]...] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...]

思路:使用归并排序,排序的内容就是返回值结构List<List<Integer>>。

class Solution {
    public List<List<Integer>> getSkyline(int[][] buildings) {
        if (buildings.length == 0) return new ArrayList<>();
        return segment(buildings, 0, buildings.length-1);
    }

    public List<List<Integer>> segment(int[][] buildings, int l, int r){
        List<List<Integer>> resList = new ArrayList<>();
        if(l == r){
            resList.add(Arrays.asList(buildings[l][0], buildings[l][2])); //左上点
            resList.add(Arrays.asList(buildings[l][1], 0)); //右下点
            return resList;
        }

        int mid = (l + r) >> 1;
        List<List<Integer>> leftList = segment(buildings, l, mid);
        List<List<Integer>> rightList = segment(buildings, mid+1, r);

        //merge
        int lIndex = 0, rIndex = 0;
        int lx, ly, rx, ry;
        int curLeftH = 0, curRightH = 0; //当前左、右List的高度
        while(lIndex < leftList.size() || rIndex < rightList.size()) {
            if (lIndex >= leftList.size()) {
                resList.add(rightList.get(rIndex++));
            } else if(rIndex >= rightList.size()) {
                resList.add(leftList.get(lIndex++));
            } else {
                lx = leftList.get(lIndex).get(0);
                ly = leftList.get(lIndex).get(1);
                rx = rightList.get(rIndex).get(0);
                ry = rightList.get(rIndex).get(1);
                //先合并靠左边的点
                if (lx < rx) { //合并lx
                    //判断应该加入的高度
                    if(ly > curRightH) { //无论以前的高度是curRigthH还是curLeftH,现在都有新高度ly了(注意ly不可能=curLeftH)
                        resList.add(leftList.get(lIndex));
                    } else if(curLeftH > curRightH ) { //以前左面比右面高,现在左面比有面矮,所以要把右面的高度加入
                        resList.add(Arrays.asList(lx, curRightH));
                    }
                    curLeftH = ly;
                    lIndex++;
                } else if (lx > rx) {//合并rx
                    if(ry > curLeftH) { 
                        resList.add(rightList.get(rIndex));
                    } else if(curRightH > curLeftH ) { 
                        resList.add(Arrays.asList(rx, curLeftH));
                    }
                    curRightH = ry;
                    rIndex++;
                } else { //在同一点,一起合并
                    if(ly >= ry && ly != (curLeftH > curRightH ? curLeftH: curRightH)) {
                        resList.add(leftList.get(lIndex));
                    } else if (ly <= ry && ry != (curLeftH > curRightH ? curLeftH: curRightH)){
                        resList.add(rightList.get(rIndex));
                    }
                    curLeftH = ly;
                    curRightH = ry;
                    lIndex++;
                    rIndex++;
                }
            }
        }
        return resList;
    }
}
原文地址:https://www.cnblogs.com/qionglouyuyu/p/13438946.html