天际线问题(大顶堆,树)

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

Buildings Skyline Contour

每个建筑物的几何信息用三元组 [Li,Ri,Hi] 表示,其中 LiRi 分别是第 i 座建筑物左右边缘的 x 坐标,Hi 是其高度。可以保证 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAXRi - 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], ...]

思路
对于图中的例子,我们仔细观察一下拐点出现具备哪些条件

  1. 新出现的矩形高度大于原矩形高度(蓝红矩形)
  2. 最大高度对应的矩形处理完之后,当前的最大高度(红绿矩形)

对于上面两个条件,进一步都可以总结为:当前未处理完的矩形中最大高度发生了变化。

因此,我们将矩形拆分成左上角和右上角的坐标,为了表明某个矩形是否处理完,我们将左上角的纵坐标记为负值,右上角纵坐标为正值,于是只要碰到某个负值时,就表明碰到一个新的矩形,同理,负值即表明某个矩形处理完了。

之后,我们对上述坐标进行排序,排序时 优先横坐标从小到大即x轴从左到右,横坐标相同时,纵坐标也从小到大(对于新的矩形,绝对值大的先处理)

同时为了得到最大高度,我们维护一个大顶堆,通过优先队列实现:

  1. 每当遇到左上角坐标,即处理一个新的矩形,将其高度加入堆中,判断此时堆顶元素(最大高度)是否发生变化,如果发生变化意味着需要添加拐点(如图中蓝色矩形中的红色矩形左上角坐标),未变化则表明新的矩形被某个矩形所覆盖,不做任何处理(如红色矩形中的绿色矩形左上角坐标);
  2. 当遇到右上角坐标时,说明某个矩形处理完,将其对应高度移除,此时再次判断堆顶元素是否变化,如果变化,说明最大高度对应的矩形被移除了,此时需要根据当前的堆顶元素添加拐点(如红色填入绿色矩形左上角坐标后,此时又遇到红色右上角坐标,将红色高度移除后,最大高度变为绿色矩形的高度)
class Solution {
    class Node{
        //横坐标
        int x;
        //纵坐标
        int y;
        //矩形序号
        int num;
        //状态信息 true表示开始 false表示结束
        boolean status;
    }
    public List<List<Integer>> getSkyline(int[][] buildings) {
         List<List<Integer>> list= new ArrayList<List<Integer>>();
        if(buildings==null || buildings.length==0){
            return list;
        }
         Node[] nodes = new Node[buildings.length*2];
         int j=0;
         for(int i=0;i<buildings.length;i++){
             Node node1 = new Node();
             int[] arr = buildings[i];
             node1.x = arr[0];
             node1.y = arr[2];
             node1.num = i;
             node1.status = true;
             nodes[j]=node1;
             j++;
             Node node2 = new Node();
             node2.x = arr[1];
             //设置成负数,这样排序的时候最大的会被排在后面,后续的结果才能正确
             node2.y = -arr[2];
             node2.num = i;
             node2.status = false;
             nodes[j]=node2;
             j++;
         }
        //对nodes数组进行排序 先x小到大 若x相同 ,y从大到小
        heapSort(nodes.length-1,nodes);
        for(int i=nodes.length-1;i>=1;i--){
            swap(0,nodes,i);
            heapSortChild(0,i-1,nodes);
        }
         //优先队列,大顶堆
        //默认容量是11,因此也创建一个容量为11
        Queue<Integer> pq = new PriorityQueue<Integer>(11,new Comparator<Integer>(){
            @Override
            public int compare(Integer i1,Integer i2){
                return i2-i1;
            }
        });
        pq.add(nodes[0].y);
       
        List<Integer> listChild = new ArrayList<Integer>();
        listChild.add(nodes[0].x);
        listChild.add(nodes[0].y);
        list.add(listChild);
        int maxHeight = pq.peek();
        for(int i=1;i<nodes.length;i++){
            if(!nodes[i].status){
                pq.remove(-nodes[i].y);
                Integer cur = pq.peek();
                if(cur==null){
                    cur=0;
                }
                if(cur!=maxHeight){
                    //说明移除了高的 需要加入点
                    listChild = new ArrayList<Integer>();
                    listChild.add(nodes[i].x);
                    listChild.add(cur);
                    maxHeight = cur;
                    list.add(listChild);
                }
            }
            if(nodes[i].status){
                pq.add(nodes[i].y);
                if(pq.peek()!=maxHeight){
                     //说明加入了高的 需要加入点
                    listChild = new ArrayList<Integer>();
                    listChild.add(nodes[i].x);
                    listChild.add(pq.peek());
                    maxHeight = pq.peek();
                    list.add(listChild);

                }
            }
        }
        return list;
        
    }
    private void heapSortChild(int index,int length,Node[] nodes){
        while(index<length){
            int left = index*2+1;
            int right = index*2+2;
            int max = index;
            if(left<=length&&nodes[max].x<nodes[left].x){
                max = left;
            }else if(left<=length&&nodes[max].x==nodes[left].x&&nodes[max].y>nodes[left].y){
                max = left;
            }
            if(right<=length&&nodes[max].x<nodes[right].x){
                max = right;
            }else if(right<=length&&nodes[max].x==nodes[right].x&&nodes[max].y>nodes[right].y){
                max = right;
            }
            if(max!=index){
                swap(max,nodes,index);
                index=max;
            }else{
                return;
            }
            
        }
    }
    private void heapSort(int length,Node[] nodes){
        for(int i=(length-1)/2;i>=0;i--){
            heapSortChild(i,length,nodes);   
        }
    }
   
    private void swap(int max,Node[] nodes,int index){
        Node node=nodes[max];
        nodes[max]=nodes[index];
        nodes[index]=node;
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值