218. 天际线问题(困难)
思路
-
题意转化
-
完整思路分析
-
multiset的使用
multiset 是关联容器的一种,是排序好的集合(元素默认升序),并且允许有相同的元素。
不能直接修改 multiset 容器中元素的值。因为元素被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。因此,如果要修改 multiset 容器中某个元素的值,正确的做法是先删除该元素,再插入新元素。
代码
class Solution {
public:
vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
vector<vector<int>> ans;
// 预处理所有坐标点,为了方便排序,令左端点的高度为负数
vector<pair<int, long>> sortedbuildings;
for(auto building : buildings){
int left = building[0];
int right = building[1];
int height = building[2];
sortedbuildings.push_back({left, -height});
sortedbuildings.push_back({right, height});
}
// 先严格按照横坐标进行「从小到大」排序
// 对于某个横坐标而言,可能会同时出现多个点,应当按照如下规则进行处理:
// 1. 优先处理左端点,再处理右端点
// 2. 如果同样都是左端点,则按照高度「从大到小」进行处理(将高度增加到优先队列中)
// 3. 如果同样都是右端点,则按照高度「从小到大」进行处理(将高度从优先队列中删掉)
sort(sortedbuildings.begin(), sortedbuildings.end(), [](auto &a, auto &b){
return a.first < b.first || (a.first == b.first && a.second * b.second < 0 && a.second < b.second) || (a.first == b.first && a.second < 0 && b.second < 0 && a.second < b.second) || (a.first == b.first && a.second > 0 && b.second > 0 && a.second < b.second);
});
multiset<int> q;
int prev = 0, cur = 0;
// 题目要求把一个完整轮廓的「右下角」那个点也取到
// 所以需要先添加一个 0
q.insert(prev);
for(auto sortedbuilding : sortedbuildings){
int point = sortedbuilding.first, height = sortedbuilding.second;
// 左端点
if(height < 0){
q.insert(-height);
}else{
q.erase(q.find(height));
}
// 获取当前最大高度
// multiset默认升序,因此最大值在最右边
cur = *q.rbegin();
// 说明当前高度发生了变化
// 此时的point就是关键点
if(cur != prev){
ans.push_back({point, cur});
prev = cur;
}
}
return ans;
}
};
参考资料
- 【宫水三叶】扫描线算法基本思路 & 优先队列维护当前最大高度
- C++ multiset