LeetCode/solutions/218. The Skyline Problem.md

54 lines
2.6 KiB
Markdown
Raw Normal View History

2020-04-06 09:50:03 +00:00
# [218. The Skyline Problem](https://leetcode.com/problems/the-skyline-problem/)
# 思路
2020-07-08 00:40:51 +00:00
**扫描法**
2020-04-06 09:50:03 +00:00
想象一下从左到右扫描如果当前扫描线穿过的building的最大高度与上一次的最大高度不一样说明遇到了转折点记录这个转折点即可。
首先为了从左到右扫描我们需要将所有边界包括左边界和右边界与高度的pair按照水平位置排序为了区分左右边界将左边界的高度存为负数建立左边界和负高度的pair
2020-07-08 00:40:51 +00:00
然后我们还需要用一个容器记录当前扫描线穿过的building的所有高度而且能够快速获得最大高度以及删除某个高度那么`multiset`就很不错。我们先插入高度0为初始高度然后开始扫描排好序的<边界,高度>pair:
* 如果遇到高度为负值的pair说明是左边界那么将高度插入multiset中
* 否则说明遇到了右边界即扫描线马上就会离开这个building应该从multiset中删除这个高度。然后取出此时multiset中最高的高度判断是否等于上一轮的最大高度若不是则表示遇到了转折需要存放到结果数组中。
> 需要注意的是这里我们使用`multiset`的`erase`方法时应该传入的是一个`iterator`而不是某个高度值,因为后者会删除所有等于该高度值的元素,而正确的应该是一次只删除一个。
2020-04-06 09:50:03 +00:00
扫描过程动图可参考[此处](https://leetcode-cn.com/problems/the-skyline-problem/solution/218tian-ji-xian-wen-ti-sao-miao-xian-fa-by-ivan_al/)
时间复杂度O(nlogn)空间复杂度O(n)
# C++
``` C++
class Solution {
public:
vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
vector<pair<int,int>>all;
vector<vector<int>>res;
multiset<int>st;
for(auto &b: buildings){
all.push_back({b[0], -b[2]}); // 左边界, -高度
all.push_back({b[1], b[2]}); // 右边界, 高度
}
sort(all.begin(), all.end()); // 将边界排好序
int pre_h = 0, cur_h;
st.insert(0);
for(auto &b: all){ // 从左至右扫描
if(b.second < 0) st.insert(-b.second);
2020-07-08 00:40:51 +00:00
else st.erase(st.find(b.second)); // 保证只删除一个
// else st.erase(b.second); // 这会删除所有b.second, 错误
2020-04-06 09:50:03 +00:00
cur_h = *st.rbegin(); // 当前最高高度
if(cur_h != pre_h){
res.push_back({b.first, cur_h});
pre_h = cur_h;
}
}
return res;
}
};
```