热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LeetCode218.TheSkylineProblem天际线问题(C++/Java)

题目:Acitysskylineistheoutercontourofthesilhouetteformedbyallthebuildingsinthatcitywhenviewe

题目:

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).

LeetCode 218. The Skyline Problem 天际线问题(C++/Java) LeetCode 218. The Skyline Problem 天际线问题(C++/Java)

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

分析:

给定一组描述建筑物的三元组[Li, Ri, Hi],其中 Li 和 Ri 分别是第 i 座建筑物左右边缘的 x 坐标,Hi 是其高度。最后求出一组坐标来表示天际线。

利用扫描线法,想象一条垂直的线从左至右扫描所有的建筑,根据图例我们不难发现,扫描到建筑的左边时,也就是有新的建筑出现,如果这个建筑的高度等于当前扫描的所有建筑的最高高度时,当前的这个坐标和高度应该加入到结果中,如果扫描到建筑的右边时,也就是有一个建筑离开了,此时扫描的建筑的最高高度发生了改变,则要把离开后的最高高度和当前坐标加入到结果中。

以上图为例,当扫描到红色建筑[3,15]时,此时我们应该保存扫描的高度集合,最大值恰好是15也就是红色建筑的高度,我们把[3,15]加入到天际线中。

当扫描到x=7时,也就是红色建筑[7,15]离开时,最高的高度由15变成了12,所以我们把[7,12]加入到天际线中。

那么为了模拟扫描建筑,实际上我们将表示建筑的线进行排序,然后再依次遍历即可。同时这里还有一个小技巧,例如红色建筑的两条线分别是[3, 15]和[7,15](x坐标,高度)我们把离开扫描线的建筑的边的高度记为负值,用来区分是进入还是离去,也就是[3, 15]和[7,-15],同时还要对所有的建筑按x的值进行升序排序,不过对于x相等的情况,我们来看一下特例:

LeetCode 218. The Skyline Problem 天际线问题(C++/Java)

也就是前一个建筑的右边和后一个建筑的左边在同一条线上,那么边的排序应该是如上图所示,也就是当x相同时,比较高度,高度大的排在前面。如果这里想不太清楚的同学,可以根据边的排序将两个建筑稍微重合一点或者分离一点,就能清楚的发现,高度长的边应该排在前面,只有这样才能正确求得天际线。

LeetCode 218. The Skyline Problem 天际线问题(C++/Java)

此外还有几种特殊情况我们来看一下:

LeetCode 218. The Skyline Problem 天际线问题(C++/Java)

这两种情况则是建筑左边横坐标相同,或者右边横坐标相同,此时排序的规则依旧是横坐标相同,按照高度的值!!降序排序,因为我们将离开的边编码为负数,所以在扫描到建筑离开的时候我们希望建筑高度低的排在前面,那么反映到编码上就是高度的值排序即可。以左边为例,如果将6,10排到6,12前面,显然不对,因为根据我们的规则,6,10也成为了天际线,但实际上只需要记录6,12即可。右边的图同理。

在存储高度时,我们使用树结构存储,可以保证删除操作在O(logn)内完成。

小技巧:存储高度集合中先加入0,当扫描建筑后没有建筑时,可以直接得到答案,而不用进行特殊处理。

程序:

C++

class Solution {
public:
    vectorint>> getSkyline(vectorint>>& buildings) {
        vectorint, int>> lines;
        vectorint>> res;
        multiset<int, greater<int>> h;
        for(auto &a:buildings){
            lines.push_back({a[0], a[2]});
            lines.push_back({a[1], -a[2]});
        }
        sort(lines.begin(), lines.end(), cmp());
        h.insert(0);
        for(auto iter = lines.begin(); iter != lines.end(); ++iter){
            int x = (*iter).first;
            int height = (*iter).second;
            int maxHeight = *h.begin();
            if(height > 0){
                if(height > maxHeight){
                    res.push_back({x, height});
                }
                h.insert(height);
            }else{
                height = -height;
                h.erase(h.find(height));
                if(maxHeight != *h.begin()){
                    res.push_back({x, *h.begin()});
                }
            }
        }
        return res;
    }
    struct cmp {
        bool operator() (const pair<int, int>& A, const pair<int, int>& B) {
            if (A.first < B.first) {
                return true;
            } else if (A.first == B.first) {
                return A.second > B.second;
            } else {
                return false;
            }
        }
    };
};

Java

import java.util.*;
class Solution {
    public List> getSkyline(int[][] buildings) {
        List> lines = new ArrayList<>();
        TreeMap h = new TreeMap<>(new Comparator() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        List> res = new ArrayList<>();
        for(int[] a:buildings){
            lines.add(new Pair(a[0], a[2]));
            lines.add(new Pair(a[1], -a[2]));
        }
        Collections.sort(lines, new Comparator>() {
            @Override
            public int compare(Pair o1, Pair o2) {
                int x1 = o1.getKey();
                int y1 = o1.getValue();
                int x2 = o2.getKey();
                int y2 = o2.getValue();
                if(x1 != x2)
                    return x1 - x2;
                else{
                    return y2 - y1;
                }
            }
        });
        h.put(0, 1);
        for(Pair p:lines){
            int x = (int)p.getKey();
            int height = (int)p.getValue();
            int maxHeight = h.firstKey();
            if(height > 0){
                if(height > maxHeight){
                    res.add(new ArrayList<>(Arrays.asList(x, height)));
                }
                h.put(height, h.getOrDefault(height, 0)+1);
            }else{
                height = -height;
                Integer v = h.get(height);
                if(v == 1){
                    h.remove(height);
                }else{
                    h.put(height, v-1);
                }
                if(maxHeight != h.firstKey()){
                    res.add(new ArrayList<>(Arrays.asList(x, h.firstKey())));
                }
            }
        }
        return res;
    }
}

 


推荐阅读
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • MPLS VP恩 后门链路shamlink实验及配置步骤
    本文介绍了MPLS VP恩 后门链路shamlink的实验步骤及配置过程,包括拓扑、CE1、PE1、P1、P2、PE2和CE2的配置。详细讲解了shamlink实验的目的和操作步骤,帮助读者理解和实践该技术。 ... [详细]
  • 本文介绍了[从头学数学]中第101节关于比例的相关问题的研究和修炼过程。主要内容包括[机器小伟]和[工程师阿伟]一起研究比例的相关问题,并给出了一个求比例的函数scale的实现。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
author-avatar
赵顺帆_705
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有