A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Given the locations and heights of all the buildings, return the skyline formed by these buildings collectively.
The geometric information of each building is given in the array buildings where buildings[i] = [lefti, righti, heighti]:
lefti is the x coordinate of the left edge of the ith building.righti is the x coordinate of the right edge of the ith building.heighti is the height of the ith building.You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0.
The skyline should be represented as a list of "key points" sorted by their x-coordinate in the form [[x1,y1],[x2,y2],...]. Each key point is the left endpoint of some horizontal segment in the skyline except the last point in the list, which always has a y-coordinate 0 and is used to mark the skyline's termination where the rightmost building ends. Any ground between the leftmost and rightmost buildings should be part of the skyline's contour.
Note: There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...,[2 3],[4 5],[7 5],[11 5],[12 7],...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...,[2 3],[4 5],[12 7],...]
Example 1:
Input: buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]] Output: [[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]] Explanation: Figure A shows the buildings of the input. Figure B shows the skyline formed by those buildings. The red points in figure B represent the key points in the output list.
Example 2:
Input: buildings = [[0,2,3],[2,5,3]] Output: [[0,3],[5,0]]
Constraints:
1 <= buildings.length <= 1040 <= lefti < righti <= 231 - 11 <= heighti <= 231 - 1buildings is sorted by lefti in non-decreasing order.이 문제는 '스위프 라인(Sweep Line)' 알고리즘과 '우선순위 큐(Priority Queue)'를 함께 사용하는 대표적인 예시입니다. 스카이라인의 윤곽이 바뀌는 지점, 즉 'Key Point'는 건물의 왼쪽 모서리 또는 오른쪽 모서리에서만 발생한다는 점에 착안합니다.
스위프 라인 (Sweep Line) 알고리즘이란?
기하학적 문제들을 풀기 위해, 수직 또는 수평선을 왼쪽에서 오른쪽으로 (또는 위에서 아래로) 훑고 지나가면서 선과 만나는 지점의 이벤트들을 처리하는 방식입니다.
알고리즘의 단계는 다음과 같습니다.
import java.util.*;
class Solution {
public List<List<Integer>> getSkyline(int[][] buildings) {
List<int[]> points = new ArrayList<>();
for (int[] b : buildings) {
points.add(new int[]{b[0], -b[2]}); // 시작점
points.add(new int[]{b[1], b[2]}); // 끝점
}
// x좌표 기준으로 정렬, x가 같으면 높이 기준으로 정렬
Collections.sort(points, (a, b) -> {
if (a[0] != b[0]) {
return a[0] - b[0];
} else {
return a[1] - b[1]; // 시작점이 먼저, 높이가 높은 시작점이 먼저
}
});
// 높이를 내림차순으로 관리하는 우선순위 큐 (Max Heap)
PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());
pq.offer(0); // 바닥 높이
int prevMaxHeight = 0;
List<List<Integer>> result = new ArrayList<>();
for (int[] point : points) {
int x = point[0];
int height = point[1];
if (height < 0) { // 시작점
pq.offer(-height);
} else { // 끝점
pq.remove(height);
}
int currentMaxHeight = pq.peek();
if (currentMaxHeight != prevMaxHeight) {
result.add(Arrays.asList(x, currentMaxHeight));
prevMaxHeight = currentMaxHeight;
}
}
return result;
}
}
O(N log N)O(N log N)의 시간이 걸립니다. 이후 2N개의 포인트를 순회하면서 각각 우선순위 큐에 삽입/삭제 연산을 수행하는데, 이 연산은 O(log N)이므로 총 O(N log N)이 소요됩니다. 따라서 전체 시간 복잡도는 정렬에 의해 결정됩니다.O(N)O(N)의 공간이 필요합니다.이 문제는 여러 개념을 복합적으로 활용해야 하는 고급 문제입니다. 면접관은 문제 해결 능력뿐만 아니라, 적절한 자료구조를 선택하고 그 이유를 설명하는 능력을 보고자 합니다.
- "왜 우선순위 큐를 사용했나요?"
특정 x좌표에서 겹치는 여러 빌딩 중 가장 높은 높이를O(log N)의 시간 복잡도로 효율적으로 찾기 위함이라고 답할 수 있습니다.- "Java의
PriorityQueue.remove(Object)는 O(N)인데, 성능에 문제가 되지 않을까요?"
좋은 지적입니다. 이 문제의 제약 조건 하에서는 통과되지만, 더 엄격한 성능이 요구된다면TreeMap이나 직접 구현한 균형 이진 탐색 트리를 사용하여 삭제 연산을O(log N)으로 최적화할 수 있음을 언급하면 좋은 인상을 줄 수 있습니다.- "스위프 라인 알고리즘을 다른 어떤 문제에 적용해볼 수 있을까요?"
겹치는 구간을 처리하는 문제들, 예를 들어 '회의실 배정 문제(Meeting Rooms II)'나 '구간 병합(Merge Intervals)'과 같은 문제에 응용될 수 있음을 설명할 수 있습니다.