解题报告:The Skyline Problem(画天际线)

题目出处:https://leetcode.com/problems/the-skyline-problem/

题目描述:

输入三元组[Li, Ri, Hi],代表建筑的左右坐标,以及高度,构成图A。

要求画出天际线,如B图所示,输出为[[x1,y1], [x2, y2], [x3, y3], ... ],一串二元组的集合,分别为轮廓的左端点,x代表横坐标,y代表纵坐标高度。

解题思路:

思路一:

首先,看到这道题,最最简单的思路是直接遍历每个x值,简单粗暴正面刚,将每个覆盖x值的区间合并并求出最大值,从而画出天际线,找到左端点。但这个思路显然要跪,毕竟最先想到的90%都有问题,这个思路的空间复杂度为O(m) ,时间复杂度为O(nm)(m为最大x值,n为区间个数),所以放弃这个思路,继续思考。

思路二:

1、通过简单的分析,我们可以发现,对于天际线起作用的实际上是建筑的顶边(就是上面那条边,原谅我语文表达不好),而顶边则是由左右端点确定的,所以我们只需要存储每个建筑的左右端点。考虑如何存储端点,应该将端点按照x坐标值由小到大排序,同时如果x相等,对于左端点,按照高度由低到高;对于右端点,按照高度由高到低排序。考虑使用什么数据结构存储端点,由于排序以及后面访问端点时,需要知道是左端点还是右端点。所以,我们建立Node类,使用vector存储点,同时重载sort函数进行排序(想到这里,就觉得好麻烦有没有(>﹏<))

2、将端点排序储存之后,我们一个一个访问端点。如果是左节点,就记录高度信息,如果高度大于之前的高度,就说明这个节点是个拐点,记录在ans(vector<pair<int, int>>类型)中;遇到右节点时,就需要将对应的左节点的高度删掉。

于是我们需要存储高度信息,而且还要实现按照大小排序的功能。到底用什么数据结构呢?好蛋疼......set不可以,可能会重复;vector没有排序;通过查文档,发现了priority_queue(好像老师上课讲过,果然没有好好听课0.0),但是这个没法删除,简直B了狗了,可能还需要flag标记要删除的信息,于是有需要map将flag映射到高度信息(果然是数据结构的题。。。。);继续找,发现了multiset,就决定是你了!我们用cur,pre分别代表当前及之前的最大高度,最终得到ans,就是结果。

代码如下:

 

class Solution {
private:
    struct node {
        int x, y;
        string type;
        node(int _x, int _y, string _type) : x(_x), y(_y), type(_type) {}
    };
    
public:
    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) 
    {
        vector<node> height;
        for (int i = 0; i < buildings.size(); i++) 
        {
            height.push_back(node(buildings[i][0], buildings[i][2], "LEFT"));
            height.push_back(node(buildings[i][1], buildings[i][2], "RIGHT"));
        }
        sort(height.begin(), height.end(),f);
        multiset<int> heap;
        heap.insert(0);
        vector<pair<int, int>> res;
        int pre = 0, cur = 0;
        for (int i = 0; i < height.size(); i++) 
        {
            if (height[i].type == "LEFT") 
            {
                heap.insert(height[i].y);
            } 
            else 
            {
                heap.erase(heap.find(height[i].y));
            }   
            cur = *heap.rbegin();
            if (cur != pre) 
            {
                res.push_back({height[i].x, cur});
                pre = cur;
            }
        }
        return res;
    }
    static bool f(const node &a, const node &b) 
    {
        if (a.x != b.x) 
            return a.x < b.x;
        else if (a.type == "LEFT" && b.type == "LEFT") 
            return a.y > b.y;
        else if (a.type == "RIGHT" && b.type == "RIGHT") 
            return a.y < b.y;
        else 
            return a.type == "LEFT";
    }
};

 

 

 

 

 

感觉解释的挺清楚的,就没有加注释(我不会告诉你其实是因为我懒(¬_¬))。提交到OJ上,已经可以AC。

本来呢,已经结束了,但是看了@bill_liu的博客,深受启发,手动感谢@bill_liu,@GDP。借用朋神的思路,可以将左节点的高度设为负值加入到点的集合中,这样就可以区分左右结点,而且神奇的发现,这样不需要重写sort函数,也符合比较结果(将端点按照x坐标值由小到大排序,同时如果x相等,对于左端点,按照高度由低到高;对于右端点,按照高度由高到低排序),这样我们不需要重新建Node类,只需要pair存储点即可。

修正代码:

class Solution {
public:
    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) 
    {
        vector<pair<int, int>> height;
        for (int i = 0; i < buildings.size(); i++) 
        {
            height.push_back({buildings[i][0], -buildings[i][2]});
            height.push_back({buildings[i][1], buildings[i][2]});
        }
        sort(height.begin(), height.end());
        multiset<int> heap;
        heap.insert(0);
        vector<pair<int, int>> res;
        int pre = 0, cur = 0;
        for (int i = 0; i < height.size(); i++) 
        {
            if (height[i].second < 0) 
            {
                heap.insert(-height[i].second);
            } 
            else 
            {
                heap.erase(heap.find(height[i].second));
            }   
            cur = *heap.rbegin();
            if (cur != pre) 
            {
                res.push_back({height[i].first, cur});
                pre = cur;
            }
        }
        return res;
    }
};

 

代码简洁了许多,有没有~

 

总结:

这个题确实很难,首先需要抽象出来线段,点,以及明确记录的是什么情况下的点。还有就是选取合适的数据结构,包括使用正负区分左右端点。

嗯,就这样了,(撸完大作业)有时间再想别的思路,晚安~~

 

同刘斌,为何对于这道题,从OJ上的提交数据来看,用python,java等写出来的运行速度比用C,C++写出来的快得多?求大神解答

此博客中的内容均为原创或来自网络,不用做任何商业用途。欢迎与我交流学习,我的邮箱是lsa0924@163.com

 

 

posted @ 2015-11-30 00:48  华丽的sky  阅读(2978)  评论(0编辑  收藏  举报