1. 程式人生 > 實用技巧 >[LeetCode] 218. The Skyline Problem

[LeetCode] 218. The Skyline Problem

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 aregiven the locations and height of all the buildingsas shown on a cityscape photo (Figure A), write a program tooutput the skylineformed by these buildings collectively (Figure B).

The geometric information of each building is represented by a triplet of integers[Li, Ri, Hi], whereLiandRiare the x coordinates of the left and right edge of the ith building, respectively, andHiis its height. It is guaranteed that0 ≤ Li, Ri ≤ INT_MAX,0 < Hi ≤ INT_MAX, andRi - Li > 0. You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0.

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] ].

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.

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] ].

Notes:

  • The number of buildings in any input list is guaranteed to be in the range[0, 10000].
  • The input list is already sorted in ascending order by the left x positionLi.
  • The output list must be sorted by the x position.
  • 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], ...]

天際線問題。題意不難理解,我這裡提及一些重點。首先,天際線都是一些橫的線段,你會發現最後要求的輸出不是線段,而是一些點的座標。這些點橫著看,都是一些線段的起點;同時這些點一定是在高度有變化的座標發生的,但是也分情況,如果高度變大,那麼輸出的點是高度更大的點;如果高度是變小的,那麼輸出的點是高度更小的。

思路是掃描線 + 最大堆。因為inputbuildings的形式是[left, right, height],表示的是每一個房子的左邊緣,右邊緣和高度。我們首先將input buildings放入一個list buildLines,做一個轉換,把每個房子的左邊緣和右邊緣的高度拆分出來,這樣我在buildLines裡面存的都是一些邊緣和他們各自的高度。但是為了區分左邊緣和右邊緣,我把左邊緣的高度暫時標記成負數。最後我再把buildLines按照高度做一個排序,這樣左邊緣會相對靠前(都是負數);如果邊緣下標一樣,高度較小的靠前。注意這個地方很巧妙地用負數區分了左邊緣的高度和右邊緣的高度,在排序的時候也確保在遍歷左邊緣的時候,會先處理高度更高的左邊緣,而遇到右邊緣的時候,會先處理高度更低的。同時我們需要一個最大堆maxHeap和一個變數preHighest記錄之前最高的高度是多少。

再次遍歷buildLines,當遇到一個左邊緣的時候(< 0),我們把他放到最大堆,但如果是一個右邊緣,我們直接從最大堆拋棄這個邊。此時如果堆頂元素curHeight跟之前一個最大高度preHighest不同的話,堆頂元素curHeight就是一條天際線的起點,他的下標就是當前遍歷到的左邊緣的下標。把這個結果加入結果集之後,記得更新preHighest。

時間O(n^2)

空間O(n)

Java實現

 1 class Solution {
 2     public List<List<Integer>> getSkyline(int[][] buildings) {
 3         List<List<Integer>> res = new ArrayList<>();
 4         List<int[]> buildLines = new ArrayList<>();
 5         for (int[] points : buildings) {
 6             // build[0, 1, 2]
 7             // build[left, right, height]
 8             buildLines.add(new int[] { points[0], -points[2] });
 9             buildLines.add(new int[] { points[1], points[2] });
10         }
11         // 從左往右再從小到大排序
12         // buildLines[0, 1]
13         // buildLines[bound, height]
14         Collections.sort(buildLines, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);
15         PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
16         maxHeap.add(0);
17         int preHighest = 0;
18         for (int[] points : buildLines) {
19             if (points[1] < 0) {
20                 maxHeap.add(-points[1]);
21             } else {
22                 maxHeap.remove(points[1]);
23             }
24             int curHeight = maxHeap.peek();
25             if (curHeight != preHighest) {
26                 res.add(Arrays.asList(points[0], curHeight));
27                 preHighest = curHeight;
28             }
29         }
30         return res;
31     }
32 }

LeetCode 題目總結