|
4 | 4 | import java.util.Arrays;
|
5 | 5 | import java.util.List;
|
6 | 6 | import java.util.TreeMap;
|
7 |
| -/**A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Now suppose you are given the locations and height of all the buildings as shown on a cityscape photo (Figure A), write a program to output the skyline formed by these buildings collectively (Figure B). |
| 7 | +/** |
| 8 | + * 218. The Skyline Problem |
8 | 9 | *
|
9 |
| - * The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX, and Ri - Li > 0. You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0. |
10 |
| -
|
11 |
| - For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] . |
12 |
| -
|
13 |
| - The output is a list of "key points" (red dots in Figure B) in the format of [ [x1,y1], [x2, y2], [x3, y3], ... ] that uniquely defines a skyline. A key point is the left endpoint of a horizontal line segment. Note that the last key point, where the rightmost building ends, is merely used to mark the termination of the skyline, and always has zero height. Also, the ground in between any two adjacent buildings should be considered part of the skyline contour. |
| 10 | + * A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. |
| 11 | + * Now suppose you are given the locations and height of all the buildings as shown on a cityscape photo (Figure A), |
| 12 | + * write a program to output the skyline formed by these buildings collectively (Figure B). |
| 13 | + * |
| 14 | + * The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], |
| 15 | + * where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, |
| 16 | + * and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX, and Ri - Li > 0. |
| 17 | + * You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0. |
| 18 | + * |
| 19 | + * For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] . |
| 20 | + * The output is a list of "key points" (red dots in Figure B) in the format of [ [x1,y1], [x2, y2], [x3, y3], ... ] |
| 21 | + * that uniquely defines a skyline. |
| 22 | + * A key point is the left endpoint of a horizontal line segment. |
| 23 | + * Note that the last key point, where the rightmost building ends, |
| 24 | + * is merely used to mark the termination of the skyline, and always has zero height. |
| 25 | + * Also, the ground in between any two adjacent buildings should be considered part of the skyline contour. |
14 | 26 |
|
15 | 27 | For instance, the skyline in Figure B should be represented as:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ].
|
16 | 28 |
|
|
30 | 42 | whenever we encounter the start of a building, we push it into the PriorityQueue, whenever we finished scanning that building, we remove it from the PriorityQueue
|
31 | 43 | Also, in the scan process, we’ll keep updating the maxHeight in the PriorityQueue if we find a new maxHeight which means the building will be overshadowed by the new higher one
|
32 | 44 |
|
33 |
| -
|
34 | 45 | Three edge cases (see the graph illustration in the above video at 12’18”):
|
35 | 46 | when two buildings have the same start point, the one with higher height shows up in the final result
|
36 | 47 | when two buildings have the same end point, the one with higher height shows up in the final result
|
37 | 48 | when the start point of one building is is also the end point of another building, the one with higher height shows up in the final result
|
38 |
| - |
39 | 49 |
|
40 | 50 | We use TreeMap over a normal PriorityQueue:
|
41 |
| -For the sake of efficiency (better time complexity), we’ll use TreeMap which supports O(logn) for remove() operation, this is the reason we choose TreeMap over a normal PriorityQueue in Java (PriorityQueue supports add() and getMaxVal() in both O(logn) time, however, for remove(), it does NOT.) |
| 51 | +For the sake of efficiency (better time complexity), we’ll use TreeMap which supports O(logn) for remove() operation, |
| 52 | + this is the reason we choose TreeMap over a normal PriorityQueue in Java (PriorityQueue supports add() and getMaxVal() in both O(logn) time, however, for remove(), it does NOT.) |
42 | 53 | But TreeMap in Java supports all the three operations in O(logn) time.*/
|
43 | 54 |
|
44 | 55 | public class _218 {
|
45 | 56 |
|
46 |
| - class BuildingPoint implements Comparable<BuildingPoint> { |
47 |
| - int x; |
48 |
| - boolean isStart; |
49 |
| - int h; |
| 57 | + public static class Solution1 { |
50 | 58 |
|
51 |
| - public BuildingPoint(int x, boolean isStart, int h) { |
52 |
| - this.x = x; |
53 |
| - this.h = h; |
54 |
| - this.isStart = isStart; |
55 |
| - } |
| 59 | + class BuildingPoint implements Comparable<BuildingPoint> { |
| 60 | + int x; |
| 61 | + boolean isStart; |
| 62 | + int h; |
| 63 | + |
| 64 | + public BuildingPoint(int x, boolean isStart, int h) { |
| 65 | + this.x = x; |
| 66 | + this.h = h; |
| 67 | + this.isStart = isStart; |
| 68 | + } |
56 | 69 |
|
57 |
| - @Override |
58 |
| - public int compareTo(BuildingPoint o) { |
59 |
| - if (this.x != o.x) { |
60 |
| - return this.x - o.x; |
61 |
| - } else { |
62 |
| - if (this.isStart && o.isStart) { |
63 |
| - return o.h - this.h; |
64 |
| - } else if (this.isStart && !o.isStart) { |
65 |
| - return -this.h - o.h; |
66 |
| - } else if (!this.isStart && !o.isStart) { |
67 |
| - return this.h - o.h; |
| 70 | + @Override |
| 71 | + public int compareTo(BuildingPoint o) { |
| 72 | + if (this.x != o.x) { |
| 73 | + return this.x - o.x; |
68 | 74 | } else {
|
69 |
| - return this.h + o.h; |
| 75 | + if (this.isStart && o.isStart) { |
| 76 | + return o.h - this.h; |
| 77 | + } else if (this.isStart && !o.isStart) { |
| 78 | + return -this.h - o.h; |
| 79 | + } else if (!this.isStart && !o.isStart) { |
| 80 | + return this.h - o.h; |
| 81 | + } else { |
| 82 | + return this.h + o.h; |
| 83 | + } |
70 | 84 | }
|
71 | 85 | }
|
72 | 86 | }
|
73 |
| - } |
74 | 87 |
|
75 |
| - public List<int[]> getSkyline(int[][] buildings) { |
76 |
| - BuildingPoint[] bps = new BuildingPoint[buildings.length * 2]; |
77 |
| - int index = 0; |
78 |
| - for (int[] building : buildings) { |
79 |
| - BuildingPoint bp1 = new BuildingPoint(building[0], true, building[2]); |
80 |
| - BuildingPoint bp2 = new BuildingPoint(building[1], false, building[2]); |
81 |
| - bps[index++] = bp1; |
82 |
| - bps[index++] = bp2; |
83 |
| - } |
| 88 | + public List<int[]> getSkyline(int[][] buildings) { |
| 89 | + BuildingPoint[] bps = new BuildingPoint[buildings.length * 2]; |
| 90 | + int index = 0; |
| 91 | + for (int[] building : buildings) { |
| 92 | + BuildingPoint bp1 = new BuildingPoint(building[0], true, building[2]); |
| 93 | + BuildingPoint bp2 = new BuildingPoint(building[1], false, building[2]); |
| 94 | + bps[index++] = bp1; |
| 95 | + bps[index++] = bp2; |
| 96 | + } |
84 | 97 |
|
85 |
| - //this is one key step: |
86 |
| - Arrays.sort(bps); |
87 |
| - |
88 |
| - List<int[]> result = new ArrayList(); |
89 |
| - TreeMap<Integer, Integer> treeMap = new TreeMap(); |
90 |
| - treeMap.put(0, 1); |
91 |
| - int prevMaxH = 0; |
92 |
| - for (BuildingPoint bp : bps) { |
93 |
| - //if it's a starting point, we'll add it into the final result |
94 |
| - if (bp.isStart) { |
95 |
| - if (treeMap.containsKey(bp.h)) { |
96 |
| - treeMap.put(bp.h, treeMap.get(bp.h) + 1); |
97 |
| - } else { |
98 |
| - treeMap.put(bp.h, 1); |
| 98 | + //this is one key step: |
| 99 | + Arrays.sort(bps); |
| 100 | + |
| 101 | + List<int[]> result = new ArrayList(); |
| 102 | + TreeMap<Integer, Integer> treeMap = new TreeMap(); |
| 103 | + treeMap.put(0, 1); |
| 104 | + int prevMaxH = 0; |
| 105 | + for (BuildingPoint bp : bps) { |
| 106 | + //if it's a starting point, we'll add it into the final result |
| 107 | + if (bp.isStart) { |
| 108 | + if (treeMap.containsKey(bp.h)) { |
| 109 | + treeMap.put(bp.h, treeMap.get(bp.h) + 1); |
| 110 | + } else { |
| 111 | + treeMap.put(bp.h, 1); |
| 112 | + } |
| 113 | + } else if (!bp.isStart) { |
| 114 | + //if it's an ending point, we'll decrement/remove this entry |
| 115 | + if (treeMap.containsKey(bp.h) && treeMap.get(bp.h) > 1) { |
| 116 | + treeMap.put(bp.h, treeMap.get(bp.h) - 1); |
| 117 | + } else { |
| 118 | + treeMap.remove(bp.h); |
| 119 | + } |
99 | 120 | }
|
100 |
| - } else if (!bp.isStart) { |
101 |
| - //if it's an ending point, we'll decrement/remove this entry |
102 |
| - if (treeMap.containsKey(bp.h) && treeMap.get(bp.h) > 1) { |
103 |
| - treeMap.put(bp.h, treeMap.get(bp.h) - 1); |
104 |
| - } else { |
105 |
| - treeMap.remove(bp.h); |
| 121 | + |
| 122 | + int currMaxH = treeMap.lastKey(); |
| 123 | + if (currMaxH != prevMaxH) { |
| 124 | + result.add(new int[]{bp.x, currMaxH}); |
| 125 | + prevMaxH = currMaxH; |
106 | 126 | }
|
107 |
| - } |
108 | 127 |
|
109 |
| - int currMaxH = treeMap.lastKey(); |
110 |
| - if (currMaxH != prevMaxH) { |
111 |
| - result.add(new int[]{bp.x, currMaxH}); |
112 |
| - prevMaxH = currMaxH; |
113 | 128 | }
|
114 | 129 |
|
| 130 | + return result; |
115 | 131 | }
|
116 |
| - |
117 |
| - return result; |
118 | 132 | }
|
119 |
| - |
120 | 133 | }
|
0 commit comments