218. The Skyline Problem

本文深入探讨了天际线问题的解决策略,采用multiset和priority_queue数据结构优化算法,详细解释了如何处理建筑物起点和终点的事件,以及如何通过比较器确保正确的处理顺序。文章提供了两种方法的代码实现,并讨论了易错点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

218. The Skyline Problem

Tushar: https://www.youtube.com/watch?v=GSBLe8cKu0s
思路:

  1. sort buildingPoints,用(x, h, isStart) 来记录
  2. 按 key = height 用TreeMap来实现event heap,因为Java priority queue的insert 和look up时间是O(logn), 但delete时间更长。Java TreeMap可以做到所有都在O(logn)内。c++ multiset都是O(logn)。
  3. heap 记录了当前的所有“active height”,堆顶是当前的最高点
  4. corner events 的处理:
    3.1. 当(x, h1, isStart), (x, h2, isStart),h1 <= h2 也就是两座楼起点一样,要先加 h2
    3.2. 当(x, h1, isEnd), (x, h2, isEnd), h1 <= h2也就是两座楼结束一样,要先删除 h1
    3.3. 当(x, h1, isEnd), (x, h2, isStart),同一点一座结束一座开始,要先加入h2
    这些特殊顺序的处理都由sorting buildingPoints时定义comparator来完成
  5. 需要记录的事件包括
    4.1. 一座楼开始,且其高度大于current_max, 新楼(x, h)需要记录
    4.2. 一座楼开始,其高度已经在heap当中,对应的计数++
    4.2. 一座楼结束,且其离开导致current_max被改变,也就是堆顶的entry消失,下一个堆顶的(x, h) 需要记录

但是如果用c++中的multiset,既然允许duplicate key,不需要用TreeMap 进行计数了。只需要每次在入set之前先peek一下之前的最高点prev_max,点推进之后再看一下current_max,如果不一致肯定需要记录接下来(x, current_max)。

知识点:

  1. 用到了multiset,https://www.geeksforgeeks.org/difference-set-multiset-unordered_set-unordered_multiset/ 这里讲了set, unordered_set, multiset, unordered_multiset的区别。(但是为什么不用priority_queue? 像692一样自定义comparator,extract_max还更快, 也support duplicates https://www.geeksforgeeks.org/does-stl-priority-queue-allow-duplicate-values/)
  2. int prev_max = *active.rbegin(); 用指针提取最大值
  3. active.erase(active.find(event.second)); 这里只会删掉一个值,如果有重复并不会多项一起删除。这里如果multiset本身储存着复合structure并且存的时候带有自己的comparator的话,删除的会是排序里重复值的最后一个(332. reconstruct itinerary)

multiset::find:Notice that this function returns an iterator to a single element (of the possibly multiple equivalent elements). To obtain the entire range of equivalent elements, see multiset::equal_range.

需要的数据结构有:

  1. Point {x_cor, height, isStart}
  2. vector<Point> buildingPoints
  3. vector<pair<int, int >> result
  4. multiset<int> active

方法1:

下次implement一下

方法2:

grandyang:http://www.cnblogs.com/grandyang/p/4534586.html
简化一下数据结构,压缩成2-d向量,就不用建Point了。将isStart这个boolean存储到height的符号当中。

易错点:

  1. 先要推入一个地平线来保证最低点
class Solution {
public:
    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
        if (buildings.size() == 0) return {};
        
        vector<pair<int, int>> buildingPoints, result;
        multiset<int> active;
        
        
        for (auto a : buildings){
            // start
            buildingPoints.push_back({a[0], -a[2]});
            // end
            buildingPoints.push_back({a[1], a[2]});
        }
        
        // sort是关键
        sort(buildingPoints.begin(), buildingPoints.end());
        
        
        // 先推进一个地平线点,保证最低点
        active.insert(0);
        int prev_max = 0;
        for (auto event: buildingPoints){
            // 如果是个起点
            if (event.second < 0){
                active.insert(-event.second);
            }
            // 如果是个终点 
            else {
                active.erase(active.find(event.second));
            }
            
            int current_max = *active.rbegin();
        
            if (prev_max != current_max) {
                   result.push_back({event.first, current_max});
                   prev_max = current_max;
                }     
            }
    return result;
    }
};

二刷:

class Solution {
public:
    vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
        auto cmp = [](pair<int, int> & a, pair<int, int> & b) {
            if (a.first == b.first) {
                if (!(a.second > 0 ^ b.second > 0)) {
                    return a.second < b.second;
                }
                return a.second < 0;
            }
            return a.first > b.first;
        };
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);
        for (auto b: buildings) {
            pq.push({b[0], b[2]});
            pq.push({b[1], -b[2]});
        }
        
        vector<vector<int>> res;
        multiset<int> active;
        active.insert(0);
        while (!pq.empty()) {
            auto top = pq.top();
            pq.pop();
            
            int pos = top.first;
            int height = abs(top.second);
            bool start = top.second > 0;
            
            if (start) {
                if (*active.rbegin() < height) {
                    res.push_back({pos, height});
                }
                active.insert(height);
            }
            else {
                active.erase(active.find(height));
                if (*active.rbegin() < height) {
                    res.push_back({pos, *active.rbegin()});
                }
            }
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值