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 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).
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.
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 position
Li
. - 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], ...]
https://leetcode.com/problems/the-skyline-problem/
先简化问题,其实就是求大楼轮廓的拐点。
如果用暴力求解,可以开一个大楼高度的数组,记录横坐标每一点上最高的高度,根据高度的最大值可以直接求出结果。
暴力坐标过大的情况下出错,本题最大的坐标是MAX_INT,开不出这么大的数组:
1 public static List<int[]> getSkyline_RuntimeError(int[][] buildings) { 2 int[] height = new int[10000]; 3 List<int[]> result = new ArrayList<int[]>(); 4 int max = 0; 5 for(int i = 0; i < buildings.length; i++){ 6 int leftX = buildings[i][0], rightX = buildings[i][1], h = buildings[i][2]; 7 for(int j = leftX; j <= rightX; j++){ 8 height[j] = Math.max(height[j], h); 9 } 10 max = Math.max(max, rightX); 11 } 12 for(int i = 1; i < max; i++){ 13 if(height[i] > height[i - 1]){ 14 int[] tmp = new int[]{i, height[i]}; 15 result.add(tmp); 16 } else if(height[i] < height[i - 1]){ 17 int[] tmp = new int[]{i - 1, height[i]}; 18 result.add(tmp); 19 } 20 } 21 if(max != 0){ 22 int[] tmp = new int[]{max, 0}; 23 result.add(tmp); 24 } 25 return result; 26 }
换一种思路,用一个大根堆维护最高的高度,增加或删除一幢大楼时,维护高度Heap。
结果只跟大楼顶边有关,只需把这条边的两个顶点加入list中,还要标示出是start还是end。
start的点加入到堆中,end从堆中移出。
list的顺序直接影响到结果,排序依照:
1. x坐标从小到大
2. start在前,end在后 (顶边重合的case --> input : {{0,2,3},{2,5,3}}, output : [[0,3],[5,0]])
3. 1和2都相同时,start节点的y从大到小,end节点的y从小到大 (左右边重合的case --> input : {{1,2,1},{1,2,2},{1,2,3}}, output : [[1,3],[2,0]])
排序后遍历,与上次最高高度不同的点就是拐点。
https://leetcode.com/discuss/67995/accepted-solution-maxheap-interval-overlapping-explained
AC代码:
1 public class Solution { 2 3 public static List<int[]> getSkyline(int[][] buildings) { 4 class Node{ 5 private int x, y; 6 private boolean isStart; 7 Node(int x, int y, boolean isStart){ 8 this.x = x; 9 this.y = y; 10 this.isStart = isStart; 11 } 12 } 13 14 List<int[]> result = new ArrayList<int[]>(); 15 Queue<Integer> heap = new PriorityQueue<Integer>(Collections.reverseOrder()); 16 heap.add(0); 17 List<Node> arr = new ArrayList<Node>(); 18 for(int i = 0; i < buildings.length; i++){ 19 arr.add(new Node(buildings[i][0], buildings[i][2], true)); 20 arr.add(new Node(buildings[i][1], buildings[i][2], false)); 21 } 22 Collections.sort(arr, new Comparator<Node>(){ 23 public int compare(Node a, Node b) { 24 if(a.x != b.x){ 25 return a.x - b.x; 26 }else if(a.isStart != b.isStart){ 27 return a.isStart ? -1 : 1; 28 }else{ 29 if(a.isStart){ 30 return b.y - a.y; 31 }else{ 32 return a.y - b.y; 33 } 34 } 35 } 36 }); 37 38 int previousHeight = -1; 39 for(int i = 0; i < arr.size(); i++){ 40 Node curr = arr.get(i); 41 if(curr.isStart){ 42 heap.add(curr.y); 43 }else{ 44 heap.remove(curr.y); 45 } 46 47 int maxHeight = heap.peek(); 48 if(curr.y >= maxHeight && previousHeight != maxHeight){ 49 result.add(new int[]{curr.x, maxHeight}); 50 } 51 previousHeight = maxHeight; 52 } 53 return result; 54 } 55 }