天际线问题

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

图见leetcode

 

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

思路:

将每个矩形表示为左上端点与右上端点,(进入X,h)与(出口X,-h)

将出口节点表示为原高度的负数,便于将之前的起点节点出栈

同时使用优先级队列表示大根堆,每次遇到左端点就入队,表示进入这个矩形遮盖的范围,每次遇到右端点就出队,表示移出了矩形,当进入一个矩形或走出一个矩形就获得当前堆的最大高度,若高度发生了改变,说明遇到了拐点,那么就将这个点加入到结果集

注意点:

当堆为空时,说明刚刚进行了出队操作,需要判断是否为最后一个节点,如果不是最后一个节点,需要注意下一个入堆的点的X坐标不能与现在出队的点的X坐标相同,如果坐标不同,说明矩形之前出现了空隙,拐点位于地平线,现在高度为0,将此拐点入结果集,如果坐标相同,说明是相连接的两个矩形,那么不对高度做任何操作。

class Solution {
    public List<int[]> getSkyline(int[][] buildings) {
        List<int[]> results = new ArrayList<>();
        List<int[]> height = new ArrayList<>();
        for(int i=0;i<buildings.length;i++){
            height.add(new int[]{buildings[i][0],buildings[i][2]});
            height.add(new int[]{buildings[i][1],-buildings[i][2]});
        }
        
        Collections.sort(height,new Comparator<int[]>(){
            public int compare(int[] o1,int[] o2){
                if(o1[0]!=o2[0]){
                    return o1[0]-o2[0];
                }else{
                    return o2[1]-o1[1];
                }
            }
        });
        
        Queue<Integer> queue = new PriorityQueue<>(10000,new Comparator<Integer>(){
            public int compare(Integer o1,Integer o2){
                return o2-o1;
            }
        });
        
        int preMax = 0;
        int curMax = 0;

        for(int i=0;i<height.size();i++){
            int[] h = height.get(i);
  
            //System.out.println(h[0]+" "+h[1]);
            if(h[1]>0){
                queue.add(h[1]);
            }else{
                queue.remove(-h[1]);
            }
                        
    
            if(!queue.isEmpty()){
                curMax = queue.peek();
            }else if(i+1<height.size()){
                int[] hNext = height.get(i+1);
                if(h[0]!=hNext[0]){
                    curMax=0;
                }
            }else{
                curMax=0;
            }
            //System.out.println(curMax);
            if(curMax!=preMax){
                preMax = curMax;
                results.add(new int[]{h[0],curMax});
            }

       
        }
        
        return results;
        
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值