题目:
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).
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_MAX
, 0 < 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], ...]
想了半天没想出来,感觉真的是一道烧脑题。
自己的思考能力、逻辑能力也还是很有待提升。
这道题的原理就是,寻找有效的拐点。该点满足不被其他任何building覆盖,或者为一个building右部的拐点。
一个building具有的覆盖力从左边起持续到右边结束。当右边结束的时候,该building对有部分的所有building都拾取了覆盖力。
因此,我们先将所有的building的左边线段和右边线段分开,将位置和高度信息以pair的形式存入vector中。再将所有线段按照位置的先后排序。
然后遍历这些线段。当遍历到开始(building的左边线段)线段时,将其加入multiset容器m中,当遍历到结束线段时,将容器m中对与之应的开始线段删除。m中存的这些线段实际上就代表了当前仍然具有覆盖力的building。
我们在插入新线段判断,该线段是否高于m中最高的线段,若高于,则说明当前的这个building的左上点不会被任何其他building覆盖,为有效的点,因此加入结果集中。
在删除线段时,若删除的线段高于剩余所有线段的高度。则说明改线段的右边会出现有效拐点。将该拐点加入结果集中。
class Solution {
public:
vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
vector<pair<int, int>> res;
if (buildings.size() == 0) return res;
vector<pair<int,int>> points;
for (int i = 0; i < buildings.size(); i++) {
//make sure that when two point's have the same height and position, the start point will go before end point.
points.push_back(make_pair(buildings[i][0], -buildings[i][2]));
points.push_back(make_pair(buildings[i][1], buildings[i][2]));
}
sort(points.begin(), points.end());
multiset<int> live{0};
int pre = 0;
for (int i = 0; i < points.size(); i++) {
if (points[i].second > 0) { // met the end point.
live.erase(live.find(points[i].second));
int maxH = *live.rbegin();
if (points[i].second > maxH)
res.push_back(make_pair(points[i].first, maxH));
}
else {
int maxH = *live.rbegin();
if (maxH < -points[i].second) res.push_back(make_pair(points[i].first,-points[i].second));
live.insert(-points[i].second);
}
}
return res;
}
};
还有一种与该方法非常相似的,不过比较复杂,它没有将每个building的左右线段拆开。而是用一个priority_queue来装builidng,里面的building按照右x的优先级排列。并且利用一个multiset记录当前queue中的building的高度。当queue中的building失去覆盖力时(即其右x小于 当前的building的左x),则pop出该building,并删除multiset中对应的高度值。
code:
class Solution {
public:
vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
vector<pair<int, int>> res;
vector<vector<pair<int,int>>> s;
if (buildings.size() == 0) return res;
for (auto i : buildings) {
vector<pair<int,int>> kps;
kps.push_back(make_pair(i[0],i[2]));
kps.push_back(make_pair(i[1], 0));
s.push_back(kps);
}
return getSkyline(s);
}
vector<pair<int,int>> merge(vector<pair<int,int>> s1, vector<pair<int,int>> s2){
int i1 = 0, i2 = 0;
int pre[2] = {0};
vector<pair<int,int>> res;
while (i1 < s1.size() && i2 < s2.size()) {
int h = 0, x = 0;
if (s1[i1].first < s2[i2].first) {
h = max(pre[1], s1[i1].second);
x = s1[i1].first;
pre[0] = s1[i1++].second;
}
else if (s1[i1].first > s2[i2].first){
h = max(pre[0], s2[i2].second);
x = s2[i2].first;
pre[1] = s2[i2++].second;
}
else { //注意判断当将x重合的情况
x = s1[i1].first;
h = s1[i1].second >= s2[i2].second ? s1[i1].second : s2[i2].second;
pre[0] = s1[i1++].second;
pre[1] = s2[i2++].second;
}
if (res.size() > 0 && res.rbegin()->second == h) continue;
res.push_back(make_pair(x, h));
}
while (i1 < s1.size()) {
res.push_back(s1[i1++]);
}
while (i2 < s2.size()) {
res.push_back(s2[i2++]);
}
return res;
}
vector<pair<int,int>> getSkyline(vector<vector<pair<int,int>>> s) {
if (s.size() == 1) return s[0];
return merge(getSkyline(vector<vector<pair<int,int>>>(s.begin(), s.begin() + s.size() / 2)),
getSkyline(vector<vector<pair<int,int>>>(s.begin() + s.size() / 2, s.end())));
}
};