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;
}