题目描述
城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度,请返回由这些建筑物形成的 天际线 。
每个建筑物的几何信息由数组 buildings 表示,其中三元组 buildings[i] = [lefti, righti, heighti] 表示:
- lefti 是第 i 座建筑物左边缘的 x 坐标。
- righti 是第 i 座建筑物右边缘的 x 坐标。
- heighti 是第 i 座建筑物的高度。
天际线 应该表示为由 “关键点” 组成的列表,格式 [[x1,y1],[x2,y2],…] ,并按 x 坐标 进行 排序 。关键点是水平线段的左端点。列表中最后一个点是最右侧建筑物的终点,y 坐标始终为 0 ,仅用于标记天际线的终点。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。
注意:输出天际线中不得有连续的相同高度的水平线。例如 […[2 3], [4 5], [7 5], [11 5], [12 7]…] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[…[2 3], [4 5], [12 7], …]
示例 1:
输入:buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
输出:[[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]]
解释:
图 A 显示输入的所有建筑物的位置和高度,
图 B 显示由这些建筑物形成的天际线。图 B 中的红点表示输出列表中的关键点。
示例 2:
输入:buildings = [[0,2,3],[2,5,3]]
输出:[[0,3],[5,0]]
扫描线算法:通用做法是划分区间,每个区间内取值。在本题目中划分等长的区间,每个区间内取高度最高的点。第一条线是横坐标是2,纵坐标是平行于y轴的直线,第二条线是3,第三条是7……
遍历节点时有两种情况,
- 一种是遍历到的是左端点,如果该端点的高度大于当前位置的最大高度,那么将该左端点加入结果集中;
- 另一种是遍历到的是右端点,需要移除右端点的高度,然后将该高度与剩余的最大高度相比较,如果该高度大于最大高度,那么加入结果集。
- 这两种情况加入结果集是不同的,对于第一种情况加入其左端点以及其高度,对于右端点加入其右端点的值以及剩余节点的最大高度。
还有一个问题是如何排序来满足以下几种情况:
图片来源使用的是pair来对高度进行排序,排序规则先按照第一个位置的值排序,如果第一个位置的值相等就按照第二个位置的值的大小排序。插入到pair中的值是这样的,对于左端点插入高度时是负数,对于右端点插入高度时是正数。
- 如果出现两个左端点相等的情况,比较的是负数,也就意味着高度大的排在前面,先遍历高度大的左端点,如第一个图所示。
- 对于右端点而言,要优先遍历高度小的,如第二个图所示。
- 第三张图所示的情况,因为左端点始终比右端点的高度小,因此是先遍历的左端点。
class Solution {
public:
vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
vector<vector<int>> res;
vector<pair<int,int>> points;
multiset<int> heights;
for(auto &b:buildings)
{
points.push_back({b[0],-b[2]});
points.push_back({b[1],b[2]});
}
sort(points.begin(),points.end());
heights.insert(0);
for(auto p:points)
{
if(p.second<0)
{
if(-p.second>*heights.rbegin())
{
res.push_back({p.first,-p.second});
//cout<<p.first<<" "<<-p.second<<endl;
}
heights.insert(-p.second);
}
else
{
heights.erase(heights.find(p.second));
if(p.second>*heights.rbegin())
{
res.push_back({p.first,*heights.rbegin()});
//cout<<p.first<<" "<<*heights.rbegin()<<endl;
}
}
}
return res;
}
};