[H扫描线] lc218. 天际线问题(扫描线求轮廓+边界情况+好题+算法技巧)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[H扫描线] lc218. 天际线问题(扫描线求轮廓+边界情况+好题+算法技巧)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:218. 天际线问题

题解:

2. 题目解析

有点难。

扫描线问题,还是一个求轮廓的扫描线问题…故不需要线段树维护区间查询(求周长、面积时hi使用线段树)。

从左向右扫,找到每个区间段中的高度最高的线段即可。可参考三叶姐的图:【宫水三叶】扫描线算法基本思路 & 优先队列维护当前最大高度

由于本题细节很多,代码很精巧。需要讨论的边界情况很多,才能将几个不同的情况采用相同的处理方式,所以单看代码而言,一种操作的背后其实对应了多种情况,都是需要具体情况具体分析的。请参考:T-SHLoRk 题解:LeetCode 218. The Skyline Problem


关注矩形上边上的两个点,称为该线段的入点、出点,也是扫描线问题的基础。

如果扫描线扫到入点,说明该矩形进入扫描线区间中,如果扫到出点,说明该矩形就要从扫描线区间中出去了。

需要从扫描线维护的区间中找到高度最高的线段作为顶部轮廓线,将左端点 ( x , y ) (x,y) (x,y)加入答案中即可。

需要维护每个顶部轮廓的高度,并且支持排序、插入、删除指定高度。由于本题需要删除指定高度,所以不能使用优先队列来维护高度,但可以使用 multiset 来维护,删除时要使用 find(x),而不能直接用 x

x 下标来排序,顺序扫描每个点,就相当于用 x=k 的直线来扫描每个矩形,multiset 维护顶部轮廓的高度。

  • 遇见左端点,即入点时,如果它的高度是大于所有已有高度的,说明来了个最高的矩形,该点和该高度加入答案,再将这个矩形高度加入 multiset 中。
  • 遇见右端点,即出点时,说明这个矩形要从扫描线中删除,multiset 首先将这个矩形的高度删除,在相同的 x 点处,可能有相同的出点,甚至有入点,以及与其高度相同的出点、入点等。

建议看题解:T-SHLoRk 题解:LeetCode 218. The Skyline Problem,本题好恶心啊,不想往后分析了。

自己研究的这个数据有点意思:[[0,2,1],[0,1,2],[0,1,3]],包含了入点相同,出点相同,两个情况:

  • 相同位置的进点,枚举顺序应先枚举最高的进点。当进点向内进的时候,从高往低进,如果大于当前的最高点,那么就加入答案,否则从低往高进,进一个加一个出错。
  • 相同位置的出点,枚举顺序应先枚举最低位置的出点。 当出点 x=1 外出的时候,从低往高出,只有最高点出去后,最高点下面的那个保留的最低点才被加入答案中。
  • 出点位置刚好有入点的话,先枚举入点再枚举出点。否则出点先出,答案会加入一个比当前位置更低的入点,显然错误。

建议复看的时候多看几遍吧,相同位置的三种情况分析:
在这里插入图片描述


时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n ) O(n) O(n)


情况很多,代码很精巧:

class Solution {
public:
    vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
        vector<vector<int>> res;
        vector<pair<int, int>> points;
        multiset<int> height;

        for (auto &b : buildings) {
            points.push_back({b[0], -b[2]});    // 左端点,入点。同高度入点从高到低排
            points.push_back({b[1], b[2]});     // 右端点,出点。同高度出点从低到高
        }

        sort(points.begin(), points.end());

        height.insert(0);                       // 加入 x 轴这个高度,因为最后落点会落到 x 轴
        for (auto &p : points) {
            int x = p.first, h = abs(p.second);
            if (p.second < 0) {                 // 左端点,入点
                if (h > *height.rbegin()) res.push_back({x, h});    // 高度最高,加入答案,反向迭代器
                height.insert(h);
            } else {                            // 右端点,出点
                height.erase(height.find(h));   // 删去该线段高度
                if (h > *height.rbegin()) res.push_back({x, *height.rbegin()});  // 删掉后的最高一条线段加入答案
            }
        }
        
        return res;
    }
};

以上是关于[H扫描线] lc218. 天际线问题(扫描线求轮廓+边界情况+好题+算法技巧)的主要内容,如果未能解决你的问题,请参考以下文章

综合笔试题难度 4.5/5,扫描线的特殊运用(详尽答疑)

扫描线及其应用

扫描线及其应用

扫描线算法(天际线问题)

题目地址(218. 天际线问题)

题目地址(218. 天际线问题)