[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 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).

Buildings 

Skyline Contour

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

天际线问题。

城市的 天际线 是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度,请返回 由这些建筑物形成的 天际线 。

每个建筑物的几何信息由数组 buildings 表示,其中三元组 buildings[i] = [lefti, righti, heighti] 表示:

lefti 是第 i 座建筑物左边缘的 x 坐标。
righti 是第 i 座建筑物右边缘的 x 坐标。
heighti 是第 i 座建筑物的高度。
你可以假设所有的建筑都是完美的长方形,在高度为 0 的绝对平坦的表面上。

天际线 应该表示为由 “关键点” 组成的列表,格式 [[x1,y1],[x2,y2],...] ,并按 x 坐标 进行 排序 。关键点是水平线段的左端点。列表中最后一个点是最右侧建筑物的终点,y 坐标始终为 0 ,仅用于标记天际线的终点。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。

注意:输出天际线中不得有连续的相同高度的水平线。例如 [...[2 3], [4 5], [7 5], [11 5], [12 7]...] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...]

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/the-skyline-problem
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题意不难理解,我这里提及一些重点。首先,天际线都是一些横的线段,但是最后要求的输出不是线段,而是一些点的坐标。这些点都是一些水平线段的左端点;同时这些点一定是在高度有变化的坐标发生的,但是也分情况,如果高度变大,那么输出的点是高度更大的点;如果高度是变小的,那么输出的点是高度更小的。

思路是扫描线 + 最大堆。因为 input buildings 的形式是 [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[]> lines = new ArrayList<>();
 5         // [left/right, height]
 6         for (int[] b : buildings) {
 7             lines.add(new int[] { b[0], -b[2] });
 8             lines.add(new int[] { b[1], b[2] });
 9         }
10         
11         // 靠左的在前,或者更高的在前
12         Collections.sort(lines, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);
13         PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
14         maxHeap.offer(0);
15         int preHeight = 0;
16         for (int[] points : lines) {
17             if (points[1] < 0) {
18                 maxHeap.offer(-points[1]);
19             } else {
20                 maxHeap.remove(points[1]);
21             }
22             int curHeight = maxHeap.peek();
23             if (curHeight != preHeight) {
24                 res.add(Arrays.asList(points[0], curHeight));
25                 preHeight = curHeight;
26             }
27         }
28         return res;
29     }
30 }

 

LeetCode 题目总结

posted @ 2020-10-07 16:01  CNoodle  阅读(549)  评论(0编辑  收藏  举报