218. The Skyline Problem
问题:
给定一个数组,表示楼的宽+高[x1,x2,h],求所形成的城市轮廓。
Input [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]] Output [[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]] Input [[1,2,1],[1,2,2],[1,2,3]] Output [[1,3],[2,0]]
解法:
解法一:FenwickTree
利用特点:通常的FenwickTree(如下图)所求为,前0~i个元素的和。
前面的值 贡献构成->后面的值
这里,我们利用后缀构造,求第i~∞个元素中的最大值。
后面的值(x2) 贡献构成->前面的值(x1~x2之间的值)。
- index:x2
- value:max(h)
例如,上图中
- tree[2]:表示node[2]的最大值和node[3]的最大值,再求最大值。
- tree[8]:表示node[8],node[9]...node[15]的最大值。
在本问题中,我们每读入一个楼的信息(遍历到x1的位置时),[x1,x2,h] 将上述的数据结构 update(x2, h)
更新node[x2]上的值,同时更新<x2的idx上的各个node的值。
- 当我们求,x0的最大值时,还未录入h,x0累计>x0的各个值。由于刚开始所以都为0
- 当我们求,x1.5的最大值时,已录入h,x1.5累计>x1.5的各个值,包含x2。因此为h
- 当我们求,x3的最大值时,x3累计>x3的各个值,不包含x2,因此h不能参与计算。
代码参考:
1 class FenwickTree {//Suffix sum tree (Normal is Preffix sum tree) 2 public: 3 FenwickTree(int n):tree(n+1, 0) {} 4 void update(int i, int delta) { 5 i++; 6 while(i>=1){ 7 tree[i]=max(tree[i], delta); 8 i -= lowbit(i); 9 } 10 } 11 int getSuMax(int i) {//max(i~infinite) 12 int maxres = 0; 13 i++; 14 while(i<tree.size()){ 15 maxres=max(tree[i], maxres); 16 i += lowbit(i); 17 } 18 return maxres; 19 } 20 21 private: 22 vector<int> tree; 23 int lowbit(int x) { 24 return x&(-x); 25 } 26 }; 27 28 class Event { 29 public: 30 int x;//坐标x 31 int h;//高度 32 int i;//楼号 33 }; 34 35 class Solution { 36 public: 37 static bool cmp(Event a, Event b){ 38 return (a.x == b.x)? a.h > b.h : a.x < b.x;//x坐标相同的情况下,高的排前面(防止二重输出) 39 } 40 vector<vector<int>> getSkyline(vector<vector<int>>& buildings) { 41 set<vector<int>> res; 42 vector<Event> eventlist; 43 map<int,int> posX;//映射x2排序后的index 44 int i=0, j=0; 45 for(vector<int> b:buildings){ 46 eventlist.push_back({b[0], b[2], j});//楼开始 47 eventlist.push_back({b[1], -b[2], j++});//楼结束 48 } 49 sort(eventlist.begin(), eventlist.end(), cmp); 50 for(int i=0; i<eventlist.size(); i++){ 51 posX.insert({eventlist[i].x, i}); 52 } 53 FenwickTree ftree(eventlist.size()); 54 for(Event e:eventlist){ 55 int maxh = ftree.getSuMax(posX[e.x]);//FenwickTree query 56 if(e.h > 0) { 57 int x2 = buildings[e.i][1]; 58 ftree.update(posX[x2], e.h);//FenwickTree update 59 if(abs(e.h) > maxh) res.insert({e.x, e.h}); 60 } else { 61 int maxh2 = ftree.getSuMax(posX[e.x]+1); 62 if(abs(e.h) > maxh2) res.insert({e.x, maxh2}); 63 } 64 } 65 vector<vector<int>> res1(res.begin(), res.end());//去重 66 return res1; 67 } 68 };
解法二:multiset
【参考 huahua:https://zxi.mytechroad.com/blog/tree/leetcode-218-the-skyline-problem/】
利用multiset的特性:
- insert形成排好序的set,最小值begin(),最大值rbegin()
- 允许重复元素
我们需要用到的操作:
- 插入一个数,insert(val)
- 删除某个值,erase(find(val))
- 最大值,*rbegin()
- 最小值,*begin()
插入一个数,删除一个数:时间复杂度:O(logn)
⚠️ 注意:
由于避免向结果插入多余不必要的节点,我们使用eventlist排序时,
- 首先按照x坐标排序,其次若x相同,则大楼 entering事件 先于 leave事件 处理。
- 若都是entering事件,那么更高的楼先处理。
因此有:
1 static bool cmp(Event a, Event b){ 2 return (a.x == b.x)? a.h > b.h : a.x < b.x; 3 } 4 5 sort(eventlist.begin(), eventlist.end(), cmp);
理由如下2图:
代码参考:
1 class Event { 2 public: 3 int x; 4 int h; 5 }; 6 7 class Solution { 8 public: 9 static bool cmp(Event a, Event b){ 10 return (a.x == b.x)? a.h > b.h : a.x < b.x; 11 } 12 vector<vector<int>> getSkyline(vector<vector<int>>& buildings) { 13 vector<vector<int>> res; 14 vector<Event> eventlist; 15 for(vector<int> b:buildings){ 16 eventlist.push_back({b[0], b[2]}); 17 eventlist.push_back({b[1], -b[2]}); 18 } 19 sort(eventlist.begin(), eventlist.end(), cmp); 20 for(Event e:eventlist){ 21 bool entering = e.h>=0; 22 int newh = abs(e.h); 23 int maxh; 24 if(entering){ 25 maxh = getMaxHeight(); 26 if(newh>maxh){ 27 res.push_back({e.x, newh}); 28 } 29 height.insert(newh); 30 }else{ 31 height.erase(height.find(newh)); 32 maxh = getMaxHeight(); 33 if(maxh<newh){ 34 res.push_back({e.x, maxh}); 35 } 36 } 37 } 38 return res; 39 } 40 private: 41 multiset<int> height; 42 int getMaxHeight(){ 43 if(height.size()==0) return 0; 44 else return *height.rbegin(); 45 } 46 };