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