forked from TheAlgorithms/Java
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSkylineProblem.java
More file actions
246 lines (229 loc) · 8.33 KB
/
SkylineProblem.java
File metadata and controls
246 lines (229 loc) · 8.33 KB
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
package com.thealgorithms.others;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* <h2>Skyline Problem</h2>
* <p>
* Solves the classic skyline problem using a divide-and-conquer approach. Given
* a list of buildings (each defined by left, height, right),
* computes the silhouette (skyline) formed by these buildings when viewed from
* a distance.
* </p>
* <p>
* Usage example:
*
* <pre>
* SkylineProblem sp = new SkylineProblem();
* sp.building = new SkylineProblem.Building[3];
* sp.add(1, 10, 5);
* sp.add(2, 15, 7);
* sp.add(3, 12, 9);
* List<SkylineProblem.Skyline> skyline = sp.findSkyline(0, 2);
* </pre>
* </p>
* <p>
* This class is not thread-safe.
* </p>
*/
public class SkylineProblem {
/**
* Array of buildings to process. Must be initialized before use.
*/
private Building[] building;
/**
* Number of buildings added so far.
*/
public int count;
/**
* Sets the building array to the specified size.
*
* @param size The size of the building array.
* @throws IllegalArgumentException if size is negative
*/
public void setBuilding(int size) {
if (size < 0) {
throw new IllegalArgumentException("Size must be non-negative");
}
this.building = new Building[size];
this.count = 0;
}
/**
* Adds a building with the given left, height, and right values to the
* buildings list.
*
* @param left The left x-coordinate of the building.
* @param height The height of the building.
* @param right The right x-coordinate of the building.
* @throws IllegalArgumentException if left >= right or height < 0
* @throws IllegalStateException if building array is not initialized or is
* full
*/
public void add(int left, int height, int right) {
if (building == null) {
throw new IllegalStateException("Building array not initialized");
}
if (count >= building.length) {
throw new IllegalStateException("Building array is full");
}
if (left >= right) {
throw new IllegalArgumentException("Left coordinate must be less than right coordinate");
}
if (height < 0) {
throw new IllegalArgumentException("Height must be non-negative");
}
building[count++] = new Building(left, height, right);
}
/**
* Computes the skyline for a range of buildings using the divide-and-conquer
* strategy.
*
* @param start The starting index of the buildings to process (inclusive).
* @param end The ending index of the buildings to process (inclusive).
* @return A list of {@link Skyline} objects representing the computed skyline.
* @throws IllegalArgumentException if indices are out of bounds or building
* array is null
*/
public List<Skyline> findSkyline(int start, int end) {
if (building == null) {
throw new IllegalArgumentException("Building array is not initialized");
}
if (start < 0 || end >= count || start > end) {
throw new IllegalArgumentException("Invalid start or end index");
}
// Base case: only one building, return its skyline.
if (start == end) {
List<Skyline> list = new ArrayList<>();
list.add(new Skyline(building[start].left, building[start].height));
list.add(new Skyline(building[start].right, 0)); // Add the end of the building
return list;
}
int mid = (start + end) / 2;
List<Skyline> sky1 = this.findSkyline(start, mid); // Find the skyline of the left half
List<Skyline> sky2 = this.findSkyline(mid + 1, end); // Find the skyline of the right half
return this.mergeSkyline(sky1, sky2); // Merge the two skylines
}
/**
* Merges two skylines (sky1 and sky2) into one combined skyline.
*
* @param sky1 The first skyline list. Not modified.
* @param sky2 The second skyline list. Not modified.
* @return A list of {@link Skyline} objects representing the merged skyline.
* @throws NullPointerException if either argument is null
*/
public List<Skyline> mergeSkyline(List<Skyline> sky1, List<Skyline> sky2) {
Objects.requireNonNull(sky1, "sky1 must not be null");
Objects.requireNonNull(sky2, "sky2 must not be null");
int i = 0;
int j = 0;
int h1 = 0;
int h2 = 0;
int prevHeight = 0;
List<Skyline> result = new ArrayList<>();
while (i < sky1.size() && j < sky2.size()) {
Skyline p1 = sky1.get(i);
Skyline p2 = sky2.get(j);
int x;
if (p1.coordinates < p2.coordinates) {
x = p1.coordinates;
h1 = p1.height;
i++;
} else if (p2.coordinates < p1.coordinates) {
x = p2.coordinates;
h2 = p2.height;
j++;
} else { // same x
x = p1.coordinates;
h1 = p1.height;
h2 = p2.height;
i++;
j++;
}
int maxH = Math.max(h1, h2);
if (result.isEmpty() || prevHeight != maxH) {
result.add(new Skyline(x, maxH));
prevHeight = maxH;
}
}
// Append remaining points
while (i < sky1.size()) {
Skyline p = sky1.get(i++);
if (result.isEmpty() || result.get(result.size() - 1).height != p.height || result.get(result.size() - 1).coordinates != p.coordinates) {
result.add(new Skyline(p.coordinates, p.height));
}
}
while (j < sky2.size()) {
Skyline p = sky2.get(j++);
if (result.isEmpty() || result.get(result.size() - 1).height != p.height || result.get(result.size() - 1).coordinates != p.coordinates) {
result.add(new Skyline(p.coordinates, p.height));
}
}
return result;
}
/**
* Represents a point in the skyline with its x-coordinate and height.
*/
public static class Skyline {
/** The x-coordinate of the skyline point. */
public final int coordinates;
/** The height of the skyline at the given coordinate. */
public final int height;
/**
* Constructor for the {@code Skyline} class.
*
* @param coordinates The x-coordinate of the skyline point.
* @param height The height of the skyline at the given coordinate.
*/
public Skyline(int coordinates, int height) {
this.coordinates = coordinates;
this.height = height;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Skyline skyline = (Skyline) o;
return coordinates == skyline.coordinates && height == skyline.height;
}
@Override
public int hashCode() {
return Objects.hash(coordinates, height);
}
@Override
public String toString() {
return "(" + coordinates + ", " + height + ")";
}
}
/**
* Represents a building with its left, height, and right x-coordinates.
*/
public static class Building {
/** The left x-coordinate of the building. */
public final int left;
/** The height of the building. */
public final int height;
/** The right x-coordinate of the building. */
public final int right;
/**
* Constructor for the {@code Building} class.
*
* @param left The left x-coordinate of the building.
* @param height The height of the building.
* @param right The right x-coordinate of the building.
*/
public Building(int left, int height, int right) {
this.left = left;
this.height = height;
this.right = right;
}
@Override
public String toString() {
return "Building{"
+ "left=" + left + ", height=" + height + ", right=" + right + '}';
}
}
}