Leetcode:The Skyline Problem

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode:The Skyline Problem相关的知识,希望对你有一定的参考价值。

  题目大意:太复杂了,不太想翻译。对于一丛相互交错的方形建筑,可以由一个二维数组表示各自的轮廓。求从远处观望时看到的天际线。所谓的天际线就是某一段水平区域上的等高线,其下方或者是建筑,或者是地面。求从左到右的天际线序列,且相邻天际线高度不等(等高天际线合并)。需要输出的是每个天际线的左端点位置。


 

  这道题可以通过对离散数据的压缩和线段树解答。

  首先将所有端点的x坐标进行压缩(实际上就是对其进行排序和去重,之后用排序的下标代替原来值),映射为0~O(n)个连续整数,其中n为建筑的数目。之所以要压缩数据是为了让线段树使用尽可能少的空间。这里用f表示数据的对应关系,f(x)表示x在有序数组中的下标,f(-1)(i)表示有序数组下标i处的值,f和f(-1)是互逆函数。

  之后建立自定义线段树,线段树中区间[x,x].v表示点x+0.5的高度。而[x,y].v=max([x,x].v, [x+1,x+1].v, ... , [y, y].v)。线段树还需要支持设置区间[x,y]的v的最小值的set操作和取v的最大值的get操作。通过值缓存和操作延迟的技术可以保证在O(log2(n))的时间复杂度内可以完成get和set操作。

  接下来第一步就是在线段树中建立整个图。遍历所有的建筑[l, r, h],利用线段树的set操作设置区间[f(l), f(r)]的v的最小值为h。

  之后遍历整个线段树,将所有有序的区间[x,x]转换为点(f(-1)(x), [x,x].v)加入到结果集合result中。

  最后从结果集合result中移除等高相邻的天际线。这是result就是我们要返回的结果了。

  说一下时间复杂度,压缩数据(排序和去重)和后期执行的O(n)次f映射和f(-1)映射,分别需要时间复杂度O(nlog2(n))、O(nlog2(n))、O(n)。因此构建映射关系和使用映射关系总共时间复杂度为O(nlog2(n))+O(nlog2(n))+O(n)=O(nlog2(n))。

  而线段树部分,建立线段树的时间复杂度为O(n),O(n)次set和get操作需要的时间复杂度分别为O(nlog2(n))、O(nlog2(n)),因此线段树操作占用时间复杂度为O(nlog2(n))。

  最后result移除等高相邻的天际线时间复杂度为O(n),因为只需要一次遍历。

  综合上面的结论,总的时间复杂度为“建立和使用映射关系” + “线段树操作” + “result移除” = O(nlog2(n)) + O(nlog2(n)) + O(n) = O(nlog2(n))。

  空间复杂度压缩数据时有序数组需要占用O(n)的空间复杂度,线段树O(n)的空间复杂度,result同样也是O(n)的空间复杂度,故总空间复杂度为O(n)+O(n)+O(n)=O(n)。


  下面给出代码:

技术分享
  1 class Solution {
  2     public List<int[]> getSkyline(int[][] buildings) {
  3         if (buildings.length == 0) {
  4             return new ArrayList<>();
  5         }
  6         
  7         CompressMap map = new CompressMap(buildings);
  8         SegmenetTree tree = new SegmenetTree(0, map.size() - 1);
  9 
 10         //Find all joint
 11         for (int i = 0, bound = buildings.length; i < bound; i++) {
 12             tree.setMin(map.get(buildings[i][0]), map.get(buildings[i][1] - 1), buildings[i][2]);
 13         }
 14         tree.toString();
 15         List<int[]> joints = new ArrayList<>(buildings.length);
 16 
 17         for (int i = tree.getLeftBound() + 1, bound = tree.getRightBound(); i <= bound; i++) {
 18             int height = tree.getMax(i, i);
 19             int[] joint = new int[]{map.reverse(i), height};
 20             joints.add(joint);
 21         }
 22 
 23         List<int[]> result = new ArrayList<>(buildings.length);
 24         result.add(joints.get(0));
 25         int lastHeight = joints.get(0)[1];
 26         for (int i = 1, bound = joints.size(); i < bound; i++) {
 27             int[] joint = joints.get(i);
 28             if (joint[1] == lastHeight) {
 29                 continue;
 30             }
 31             lastHeight = joint[1];
 32             result.add(joint);
 33         }
 34 
 35        //result.add(new int[]{map.get(tree.getRightBound()), 0});
 36         return result;
 37     }
 38 
 39     private static class CompressMap {
 40         int[] orderedValues;
 41         int length;
 42 
 43         public CompressMap(int[][] buildings) {
 44             orderedValues = new int[buildings.length * 4];
 45             for (int i = 0, bound = buildings.length; i < bound; i++) {
 46                 orderedValues[i << 2] = buildings[i][0];
 47                 orderedValues[(i << 2) | 1] = buildings[i][1];
 48                 orderedValues[(i << 2) | 2] = buildings[i][0] - 1;
 49                 orderedValues[(i << 2) | 3] = buildings[i][1] - 1;
 50             }
 51 
 52             Arrays.sort(orderedValues);
 53             length = 1;
 54             for (int i = 1, bound = orderedValues.length; i < bound; i++) {
 55                 if (orderedValues[i] != orderedValues[i - 1]) {
 56                     orderedValues[length++] = orderedValues[i];
 57                 }
 58             }
 59         }
 60 
 61         public int get(int val) {
 62             return Arrays.binarySearch(orderedValues, 0, length, val);
 63         }
 64 
 65         public int reverse(int index) {
 66             return orderedValues[index];
 67         }
 68 
 69         public int size() {
 70             return length;
 71         }
 72     }
 73 
 74     private static class SegmenetTree {
 75         int left;
 76         int right;
 77         int[] cacheValues;
 78         int[] opMarks;
 79 
 80         public SegmenetTree(int left, int right) {
 81             this.left = left;
 82             this.right = right;
 83 
 84             int proper = 1;
 85             int length = right - left + 1;
 86             while (proper < length) {
 87                 proper <<= 1;
 88             }
 89 
 90             cacheValues = new int[proper * 2];
 91             opMarks = new int[proper * 2];
 92         }
 93 
 94         public int getLeftBound() {
 95             return left;
 96         }
 97 
 98         public int getRightBound() {
 99             return right;
100         }
101 
102         public void setMin(int from, int to, int val) {
103             setMin(from, to, left, right, 1, val);
104         }
105 
106         private void setMin(int from, int to, int curLeft, int curRight, int index, int val) {
107             if (from > curRight || to < curLeft) {
108                 return;
109             }
110 
111             if (from <= curLeft && to >= curRight) {
112                 cacheValues[index] = Math.max(cacheValues[index], val);
113                 opMarks[index] = Math.max(opMarks[index], val);
114                 return;
115             }
116 
117             int mid = (curLeft + curRight) >> 1;
118             setMin(from, to, curLeft, mid, leftIndex(index), val);
119             setMin(from, to, mid + 1, curRight, rightIndex(index), val);
120             cacheValues[index] = Math.max(Math.max(cacheValues[leftIndex(index)], cacheValues[rightIndex(index)]), opMarks[index]);
121         }
122 
123         public int getMax(int from, int to) {
124             return getMax(from, to, left, right, 1);
125         }
126 
127         private int getMax(int from, int to, int curLeft, int curRight, int index) {
128             if (from <= curLeft && to >= curRight) {
129                 return cacheValues[index];
130             }
131 
132             if (from > curRight || to < curLeft) {
133                 return 0;
134             }
135 
136             int mid = (curLeft + curRight) >> 1;
137             return Math.max(Math.max(
138                     getMax(from, to, curLeft, mid, leftIndex(index)),
139                     getMax(from, to, mid + 1, curRight, rightIndex(index))
140             ), opMarks[index]);
141         }
142 
143         public int leftIndex(int i) {
144             return (i << 1);
145         }
146 
147         public int rightIndex(int i) {
148             return (i << 1) | 1;
149         }
150 
151         @Override
152         public String toString() {
153             StringBuilder builder = new StringBuilder();
154             for (int i = getLeftBound(); i < getRightBound(); i++) {
155                 builder.append(getMax(i, i)).append(",");
156             }
157             if (builder.length() > 0) {
158                 builder.setLength(builder.length() - 1);
159             }
160             return builder.toString();
161         }
162     }
163 }
View Code

 

以上是关于Leetcode:The Skyline Problem的主要内容,如果未能解决你的问题,请参考以下文章

OpenStack Skyline 现代化的管理界面

Skyline开发2-第一个程序

OpenStack新版UI管理skyline

OpenStack新版UI管理skyline

OpenStack新版UI管理skyline

skyline与cesium差异