-
Notifications
You must be signed in to change notification settings - Fork 5
/
TheSkylineProblem.java
138 lines (114 loc) · 5.57 KB
/
TheSkylineProblem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*https://leetcode.com/problems/the-skyline-problem/*/
class Solution {
public List<List<Integer>> getSkyline(int[][] buildings) {
List<List<Integer>> res = new ArrayList<>();
List<int[]> heights = new ArrayList<>();
// transforming buildings
for (int[] building : buildings) { // O(n)
heights.add(new int[] {building[0], -building[2]});
heights.add(new int[] {building[1], building[2]});
}
Collections.sort(heights, (a, b) -> (a[0] == b[0]) ? a[1] - b[1] : a[0] - b[0]); // O(nlogn)
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> b - a);
pq.offer(0);
int prevMax = 0;
for (int[] height : heights) { // O(n)
// priority queue operations take O(logn)
if (height[1] < 0) pq.offer(-height[1]);
else pq.remove(height[1]);
int currMax = pq.peek();
if (currMax != prevMax) {
res.add(Arrays.asList(height[0], currMax));
prevMax = currMax;
}
}
return res;
}
}
// TC: O(n) + 2 * O(n * logn) => O(n * logn)
// SC: O(n)
//Divide and Conquer approach
class Solution {
public List<List<Integer>> getSkyline(int[][] buildings) {
// Get the whole skyline from all the input buildings.
return divideAndConquer(buildings, 0, buildings.length - 1);
}
public List<List<Integer>> divideAndConquer(int[][] buildings, int left, int right) {
// If the given array of building contains only 1 building, we can
// directly return the corresponding skyline.
if (left == right) {
List<List<Integer>> answer = new ArrayList<>();
answer.add(Arrays.asList(buildings[left][0], buildings[left][2]));
answer.add(Arrays.asList(buildings[left][1], 0));
return answer;
}
// Otherwise, we shall recursively divide the buildings and
// merge the skylines. Cut the given skyline into two halves,
// get skyline from each half and merge them into a single skyline.
int mid = (right - left)/2 + left;
List<List<Integer>> leftSkyline = divideAndConquer(buildings, left, mid);
List<List<Integer>> rightSkyline = divideAndConquer(buildings, mid+1, right);
return mergeSkylines(leftSkyline, rightSkyline);
}
// Given two skylines: leftSky and rightSky, merge them into one skyline.
public List<List<Integer>> mergeSkylines(List<List<Integer>> leftSkyline, List<List<Integer>> rightSkyline) {
// Initalize leftPos=0, rightPos=0 as the pointer of lft_sky and rgt_sky.
// Since we start from the left ground, thus our current height curY = 0,
// the previous height from lft_sky and rgt_sky are also 0.
List<List<Integer>> answer = new ArrayList<>();
int leftPos = 0, rightPos = 0;
int leftPrevHeight = 0, rightPrevHeight = 0;
// Now we start to iterate over both skylines.
while (leftPos < leftSkyline.size() && rightPos < rightSkyline.size()) {
int curY;
int nextLeftX = leftSkyline.get(leftPos).get(0);
int nextRightX = rightSkyline.get(rightPos).get(0);
// If we meet lft_sky key point first, our current height
// changes to the larger one between height on left skyline
// and the previous height on right skyline. Update the
// previous height from lft_sky and increment leftPos by 1.
int curX = Math.min(nextLeftX, nextRightX);
if (nextLeftX < nextRightX) {
leftPrevHeight = leftSkyline.get(leftPos).get(1);
curY = Math.max(leftPrevHeight, rightPrevHeight);
leftPos++;
}
// If we meet rgt_sky key point first, our current height
// changes to the larger one between height on right skyline
// and the previous height on left skyline. Update the
// previous height from rgt_sky and increment rightPos by 1.
else if (nextLeftX > nextRightX) {
rightPrevHeight = rightSkyline.get(rightPos).get(1);
curY = Math.max(leftPrevHeight, rightPrevHeight);
rightPos++;
}
// If both skyline key points has same x:
// Our current height is the larger one, update the
// previous height from lft_sky and rgt_sky.
// Increment both leftPos and rightPos by 1.
else {
leftPrevHeight = leftSkyline.get(leftPos).get(1);
rightPrevHeight = rightSkyline.get(rightPos).get(1);
curY = Math.max(leftPrevHeight, rightPrevHeight);
leftPos++;
rightPos++;
}
// Discard those key points that has the same height
// as the previous one.
if (answer.isEmpty() || answer.get(answer.size()-1).get(1) != curY){
answer.add(Arrays.asList(curX, curY));
}
}
// If we finish iterating over any skyline,
// just append the rest of the other skyline to the merged skyline.
while(leftPos < leftSkyline.size()) {
answer.add(leftSkyline.get(leftPos));
leftPos++;
}
while(rightPos < rightSkyline.size()) {
answer.add(rightSkyline.get(rightPos));
rightPos++;
}
return answer;
}
}