LeetCode-218 轮廓线问题

题目描述

 

 

大意是:给出一系列矩形区域的左右端点和高度,让你输出一系列拐点。

题目太长实在不想放上来

具体见:原题

 

 

解题思路

 

第一反应

这题基本是在一个有范围的区间上进行修改

所以使用线段树可行·

 然后发现还是有一些问题需要解决

这题输入上的特性决定了:

不能单纯二分建立线段树,因为输入范围具有一定的问题:

会产生空间悲剧

于是需要使用一个映射来配合线段树

要点在于将被矩形端点分割出来的线段(如果有的话)作为普通线段树的一个点进行映射

这个映射的构建上比较需要思考

这样的话,根据输入规模: 最多10000个矩形,空间有保证

为此需要使用一个map

建立输入矩形端点到压缩后线段端点的映射

然后建立线段树

在上述操作后,我们能得到一棵线段树,并且可以将矩形端点对应到线段树端点

这样的话就可以正常使用线段树的更新和查询操作了

 随后用矩形高度更新线段树

然后根据映射来提取对应点高度

 

計画通り!

 

然而一开始的映射设计和线段树分块没想太好

最后烦透了换了方法于是还是比较快的AC了

然后一直试图把这个方法补完,未果

直到在讨论区发现映射的正确构造方法

 

附上代码:

 

  1 struct Node 
  2 {
  3     int l, r, h;
  4 };
  5 
  6 struct Segment 
  7 {
  8     int l, r;
  9     Segment(int in_l, int in_r)
 10     {
 11         l = in_l;
 12         r = in_r;
 13     }
 14 };
 15 
 16 class Solution 
 17 {
 18 public:
 19     vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) 
 20     {
 21         vector<pair<int, int>> res;
 22         vector<Segment> segments;
 23         if (buildings.size() == 0) 
 24             return res;
 25         int buildEnd = buildings.size();
 26         vector<int> nodes;
 27         for (int i = 0; i < buildEnd; i++) 
 28         {
 29             nodes.push_back(buildings[i][0]);
 30             nodes.push_back(buildings[i][1]);
 31         }
 32         sort(nodes.begin(), nodes.end());
 33         int pre = 0;
 34         int nodeSize = nodes.size();
 35         map<int, int> nodeToTree;
 36         int index;
 37         for (int i = 0; i < nodeSize; i++) 
 38         {
 39             if (pre < nodes[i]) 
 40                 segments.push_back(Segment(pre, nodes[i] - 1));
 41             segments.push_back(Segment(nodes[i], nodes[i]));
 42             index = segments.size() - 1;
 43             nodeToTree[nodes[i]] = index;
 44             pre = nodes[i] + 1;
 45         }//建立了矩形端点到特定区间的映射
 46         int segNum = segments.size();
 47         segTree = vector<Node>(segNum * 4, {0});
 48         heights = vector<int>(segNum + 1, 0);//存储高度
 49 
 50         buildTree(1, 0, segNum);//建树
 51 
 52         for (int i = 0; i < buildEnd; i++) 
 53         {
 54             setHeight(1, nodeToTree[buildings[i][0]], nodeToTree[buildings[i][1]] - 1, buildings[i][2]);
 55         }
 56 
 57         getHeights();
 58 
 59         for (int i = 0; i < segNum; i++) 
 60         {
 61             if (i == 0 && heights[i] != 0 || i != 0 && heights[i] != heights[i - 1])
 62             {
 63                 res.push_back(make_pair(segments[i].l, heights[i]));
 64             }
 65         }
 66         if (heights[segNum - 1] != 0)
 67         { 
 68             res.push_back(make_pair(segments[segNum - 1].l, 0)); 
 69         }
 70         return res;
 71     }
 72 private:
 73     vector<Node> segTree;
 74     vector<int> heights;
 75     
 76     void buildTree(int index, int l, int r) 
 77     {
 78         segTree[index].l = l;
 79         segTree[index].r = r;
 80         segTree[index].h = 0;
 81         if (l == r)
 82         {
 83             return;
 84         }
 85         int mid = (l + r) / 2;
 86         buildTree(index * 2, l, mid);
 87         buildTree(index * 2 + 1, mid + 1, r);
 88     }
 89 
 90     void update(int index) 
 91     {
 92         if (segTree[index].l == segTree[index].r)
 93         {
 94             return;
 95         }
 96         if (segTree[index * 2].h < segTree[index].h) 
 97         {
 98             segTree[index * 2].h = segTree[index].h;
 99         }
100         if (segTree[index * 2 + 1].h < segTree[index].h) 
101         {
102             segTree[index * 2 + 1].h = segTree[index].h;
103         }
104         segTree[index].h = 0;
105     }//更新儿子
106 
107     void setHeight(int index, int l, int r, int h) {
108 
109         if (segTree[index].l >= l && segTree[index].r <= r) 
110         {
111             segTree[index].h = max(segTree[index].h, h);
112             return;
113         }
114 
115         int mid = (segTree[index].l + segTree[index].r) / 2;
116         update(index);
117         if (mid >= l) 
118         {
119             setHeight(index * 2, l, r, h);
120         }
121         if (mid < r) 
122         {
123             setHeight(index * 2 + 1, l, r, h);
124         }
125     }
126 
127     void getHeights() 
128     {
129         int index;
130         queue<int> q;
131         q.push(1);
132 
133         while (!q.empty()) 
134         {
135             index = q.front();
136             q.pop();
137             update(index);//通过遍历将值分配到叶子节点上
138             if (segTree[index].l == segTree[index].r)
139             {
140                 heights[segTree[index].l] = segTree[index].h;
141                 continue;
142             }
143             q.push(index * 2);
144             q.push(index * 2 + 1);
145         }
146     }
147 };
View Code

 

只能说写完这个方法花的时间比我AC这道题不知道长多少

如果从头到尾耗在这个方法上就亏大了

 //卧槽我想太多了,直接插入更改就好了,只能说自己的第一反应从来十分诡异

 

最后心浮气躁懒得用线段树的反应

 

利用一个优先队列来实现log(n)优化

这个优先队列储存的内容是区间的信息 [ l, r, height ]

 

我们知道如下特性:

1.所有输出的点都应该在矩形端点中

2.在某一点左侧的矩形不会为其高度做贡献

3.右端点不对所在位置的高度做出任何贡献(不取右端点对应的矩形高度)

4.任意端点的取值都应该是当前位置的最高高度

 

那么我们可以看出:

由于性质1

我们需要按顺序访问矩形端点

由于性质4

我们需要取得某点处的最高高度

这一点可以通过优先队列实现

由于性质2、 3

在当前位置如果队首在该点或者该点之前结束,需被出队

可以分解任务,让左端点处理上升情况,右端点处理下降情况

且如果一个点同为左右端点时按先按照左端点推高度再按照右端点处理

又显然,遇见左端点时,对应矩形应入队

 

 

总结思路大概如下:

 

置当前节点为第一个矩形左端点
对于当前的节点: 如为右节点
退队直到当前的队首尚未于该点结束或者空队位置
根据队首高度情况以及在该点之前的高度情况向res推入<节点位置,高度> 

转到下一个矩形开始或队首矩形结束


如为左节点


将区间入队
根据队首高度情况以及在该点之前的高度情况向res推入<节点位置,高度>
转到下一个矩形开始或队首矩形结束

 

自己写的代码实现机制太丑陋不放了

 

代码:

 

struct Rect
{
    int m_left, m_right, m_height;

    Rect(const vector<int> &building)
    {
        m_left = building[0];
        m_right = building[1];
        m_height = building[2];
    }

    bool operator<(const Rect &a) const
    {
        return m_height < a.m_height;
    }

};

class Solution {
public:
    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings)
{

    if (buildings.size() == 0) 
        return {};
    
    vector<pair<int, int> > res;
    priority_queue<Rect> queue;
    int x = buildings[0][0], y = 0, p = 0;

    while (1) 
    {
        if (p == buildings.size() || x < buildings[p][0]) 
        {
            while (!queue.empty() && x >= queue.top().m_right)
                queue.pop();

            if (queue.empty()) 
            {
                y = 0;
                res.push_back(pair<int, int>(x, y));

                if (p == buildings.size())
                    break;    
                else 
                    x = buildings[p][0];
            }    
            else 
            {
                if (queue.top().m_height < y) 
                {
                    y = queue.top().m_height;
                    res.push_back(pair<int, int>(x, y));
                }

                if (p == buildings.size()) 
                    x = queue.top().m_right;
                else 
                    x = min(queue.top().m_right, buildings[p][0]);
            }
        }
        else 
        {
            while (p != buildings.size() && buildings[p][0] == x)
                queue.push(Rect(buildings[p++]));

            if (queue.top().m_height > y) 
            {
                y = queue.top().m_height;
                res.push_back(pair<int, int>(x, y));
            }
            if (p == buildings.size()) 
                x = queue.top().m_right;
            else 
                x = min(queue.top().m_right, buildings[p][0]);
        }
    }
    return res;
}
};
View Code

 

转载于:https://www.cnblogs.com/ocNflag/p/5011209.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值