題目:
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())));
}
};