1. 程式人生 > >Leetcode:The Skyline Problem

Leetcode:The Skyline Problem

ttr clas 就是 img append err mark logs etc

  題目大意:太復雜了,不太想翻譯。對於一叢相互交錯的方形建築,可以由一個二維數組表示各自的輪廓。求從遠處觀望時看到的天際線。所謂的天際線就是某一段水平區域上的等高線,其下方或者是建築,或者是地面。求從左到右的天際線序列,且相鄰天際線高度不等(等高天際線合並)。需要輸出的是每個天際線的左端點位置。


  這道題可以通過對離散數據的壓縮和線段樹解答。

  首先將所有端點的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