[Leetcode] 218. The Skyline Problem 解题报告

来源:互联网 发布:淘宝店铺怎么设置红包 编辑:程序博客网 时间:2024/06/01 10:31

题目

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

Buildings Skyline Contour

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 < Hi ≤ INT_MAX, 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], ...]

思路

这道题目算是Leetcode里面比较难的一道了,估计在实际面试中很难遇到。不过由于这算一道典型的扫描线算法(sweep line)类型的题目,所以我在这里好好总结一下网上一个非常好的算法及其代码:

1)将起点连同高度与终点连同高度各作为一个pair都统一保存到一个数组中,然后进行排序。为区分起点和终点,将起点高度设为负值,这样的好处是如果有另外一个终点和起点的x坐标一样,排序之后起点会在前面,也就是在同一个x坐标处,我们会优先处理起点。

2)采用扫描线算法依次处理我们前面排好序的数组中的元素。如果是起点,就将其高度放到multiset中;如果是终点,就在multiset中将其起点删除掉。接着还需要检查删除前后multiset中的最大值是否发生改变,如果发生改变了,则说明这个点就是边际点,将其加入结果集中。

一个实现中特别容易犯的错误就是在multiset中按值删除,这样会将所有等于这个值的点都删除。正确的做法是采用find方法找到等于该值的第一个指针,然后删除该指针,这样可以保证只删除一个。实现中的另外一个小技巧是首先在multiset中添加一个0值,这样可以保证将第一个高度不为0的建筑的起点加入结果集中。

代码

class Solution {public:    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {        vector<pair<int, int>> heights;        vector<pair<int, int>> ret;        for(int i = 0; i < buildings.size(); ++i) {            heights.push_back(make_pair(buildings[i][0], -buildings[i][2]));            heights.push_back(make_pair(buildings[i][1], buildings[i][2]));        }        sort(heights.begin(), heights.end());        multiset<int> layers;        layers.insert(0);        int pre_height = 0;        for(auto it = heights.begin(); it != heights.end(); ++it) {            if(it->second < 0) {                // a new building begin                layers.insert(-it->second);            }            else {                              // an existing building end                auto it2 = layers.find(it->second);                layers.erase(it2);            }            if(*layers.rbegin() != pre_height) {                pre_height = *layers.rbegin();                ret.push_back(make_pair(it->first, pre_height));            }        }        return ret;    }};

原创粉丝点击