1、題目描述
城市的天際線是從遠處觀看該城市中所有建築物形成的輪廓的外部輪廓。現在,假設您獲得了城市風光照片(圖A)上顯示的所有建築物的位置和高度,請編寫一個程序以輸出由這些建築物形成的天際線(圖B)。
每個建築物的幾何信息用三元組 [Li,Ri,Hi] 表示,其中 Li 和 Ri 分別是第 i 座建築物左右邊緣的 x 座標,Hi 是其高度。可以保證 0 ≤ Li, Ri ≤ INT_MAX, 0 < 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], ...]
3、題解
基本思想:掃描線算法,問題的解決的關鍵是建立合適的數據結構
一個multiset保存建築的拐角及對應高度,一個multiset保存當前掃描線上的所有建築高度,maxheight保存掃描線上最大高度,如果當前掃描線上maxheight發生改變,說明產生拐點加入到res。
- 1.將buildings所有建築中左右拐點(用正負區分左右拐點)及對應高度保存至corners
- 2.掃描corners中所有拐點
- 遇到左拐點,掃描線掃到該建築,當前建築高度加入掃描線高度heights;
- 遇到右拐點,掃描線離開該建築,掃描線高度heights去除當前建築高度。
- 如果當前掃描線高度爲空,說明沒有建築高度了,也是產生了拐點,同時更新maxheight。
- 或者因爲heights是按從小到大排序好的,如果當前掃描線maxheight不等於heights最後一個高度,說明產生拐點加入到res,同時更新maxheight。
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
class Solution {
public:
vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
//基本思想:掃描線算法,問題的解決的關鍵是建立合適的數據結構
//一個multiset保存建築的拐角及對應高度,一個multiset保存當前掃描線上的所有建築高度
//maxheight保存掃描線上最大高度,如果當前掃描線上maxheight發生改變,說明產生拐點加入到res
//1.將buildings所有建築中左右拐點(用正負區分左右拐點)及對應高度保存至corners
//2.掃描corners中所有拐點,遇到左拐點,掃描線掃到該建築,當前建築高度加入掃描線高度heights
//遇到右拐點,掃描線離開該建築,掃描線高度heights去除當前建築高度
//如果當前掃描線高度爲空,說明沒有建築高度了,也是產生了拐點,同時更新maxheight
//或者因爲heights是按從小到大排序好的,如果當前掃描線maxheight不等於heights最後一個高度,說明產生拐點加入到res,同時更新maxheight
vector<vector<int>> res;
multiset<pair<int, int>> corners; //corner保存建築的拐角及對應高度,兩棟樓的拐角可能重合所以用multiset
multiset<int> heights; //heights保存當前掃描線上的所有建築高度
int maxheight; //保存掃描線上最大高度
for (auto building : buildings)
{
corner.insert({ building[0],-building[2] }); //區分建築的左右拐角
corner.insert({ building[1],building[2] });
}
maxheight = 0;
for (auto corner : corners)
{
if (corner.second < 0)
heights.insert(-corner.second); //遇到左拐點,掃描線掃到該建築,當前建築高度加入掃描線高度
else
heights.erase(heights.find(corner.second)); //遇到右拐點,掃描線離開該建築,掃描線高度去除當前建築高度
//如果當前掃描線高度爲空,說明沒有建築高度了,也是產生了拐點,同時更新maxheight
if (heights.begin() == heights.end())
{
maxheight = 0;
res.push_back({ corner.first,maxheight });
}
//因爲multiset是按從小到大排序好的,如果當前掃描線maxheight發生改變,說明產生拐點加入到res,同時更新maxheight
else if(maxheight != *heights.rbegin())
{
maxheight = *heights.rbegin();
res.push_back({ corner.first,maxheight });
}
}
return res;
}
};
int main()
{
Solution solute;
vector<vector<int>> buildings = {
{2, 9, 10} ,{3, 7, 15},{5, 12, 12},{15, 20, 10},{19, 24, 8}
};
vector<vector<int>> res = solute.getSkyline(buildings);
for_each(res.begin(), res.end(), [](const auto v) {cout << v.front() << " " << v.back() << endl; });
return 0;
}