[LeetCode-JAVA] The Skyline Problem

题目:题目太长了,见链接-- > The Skyline Problem 

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], ...]

题意:记录每一次建筑的拐点,连续的相同高度,只记录最前面的。

思路:将每一个竖线按顺序存入,对于每一个矩形,都有高度相同的两条竖线,为了在一次扫描的时候,能将所有的竖线都加入,利用一个小技巧,将右边界的高度存为负值,恰好可以对其标记。将存入的list排序,需要自定义一个比较器比较a[],b[],如果a[0] == b[0] 说明竖线重合,这个时候应该是高的在前面,如果a[0] != b[0] 只需按照a[0]b[0]从小到大即可。

最后在遍历list的时候,为了满足note4,即连续相同的高度只记录最前即可,因此需要用到pre和cur分别记录当前的最高和上一次的最高。在判断的时候,需要维护每次当前的最大值,可以用PriorityQueue权重队列,其构造原理是大顶堆,即根节点为最小(或最大)的二叉树,默认跟为最小值,根据题意,本题需要自己定义一个比较器,根保存当前最大值(可以注意我下面说的堆个队里,是同一个概念,为了方便理解,不同的时候用了不同的说法)。

最核心的思想:扫描到左边界的时候,将高度加入到大顶堆,cur的值去peek即为当前的最大值,当cur和pre不同的时候,将坐标加入结果队列即可,当为右边界时(高度为负值),证明该矩形已经到头,在堆中去掉其高度值,如果此时队列为空,则证明此处非连续,即此时应加入的高度为0,如果不为空,则更新相应当前最高即可。

代码:

public class Solution {
    //大顶推比较器
    public class MaxCom implements Comparator<Integer> {
        public int compare(Integer a, Integer b){
            return b - a ; // 大的在堆的顶端
        }
    } 
    //数组比较器
    public class ArrayCom implements Comparator<int[]> {
        public int compare(int[] a, int[] b) {
            if(a[0] != b[0]) return a[0] - b[0];  //先按左边界进行排序
            return b[1] - a[1];  // 相等 则高的在前面
        }
    }
    public List<int[]> getSkyline(int[][] buildings) {  
        List<int[]> res = new ArrayList<int[]>();  
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11, new MaxCom());
        
        List<int[]> ver = new ArrayList<int[]>();  // 记录每一个竖线
        for(int i = 0 ; i < buildings.length ; i++){
            int[] temp = buildings[i]; 
            ver.add(new int[]{temp[0], temp[2]});  // 左边界竖线
            ver.add(new int[]{temp[1], -temp[2]});  // 右边界竖线 为了区分 存入负值
        }
        Collections.sort(ver, new ArrayCom());
        
        int cur = 0, pre = 0;
        for(int i = 0 ; i < ver.size() ; i++){
            int[] temp = ver.get(i);
            if(temp[1] > 0) {  // 左边界
                maxHeap.offer(temp[1]);  //高度入队
                cur = maxHeap.peek(); // 当前最高的
            }else { // 右边界
                maxHeap.remove(-temp[1]);  // 将对应的高度从堆中删除 这里就是右边存负值的方便之处
                cur = (maxHeap.peek() == null ? 0 : maxHeap.peek()); // 如果右边界是最后一个则高度为0,否则更新当前最高
            }
            if(cur != pre) {  // 与上一个最高的不相等
                res.add(new int[]{temp[0], cur});
                pre = cur;  // 保存当前高度为下一次的前面高度
            }
        }
        return res;       
    }
}

 参考链接:http://blog.csdn.net/xudli/article/details/46349383

原文地址:https://www.cnblogs.com/TinyBobo/p/4592061.html