题目描述:
The Skyline Problem 是一个经典的计算几何问题,通常被称为“天际线问题”。给定一组建筑物的位置和高度,我们需要找出由这些建筑物构成的天际线的轮廓。
在 LeetCode 上,该问题的编号是 218,具体描述如下:
城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度,请返回由这些建筑物形成的 天际线 。
每个建筑物的几何信息由数组 [left, right, height] 表示,其中:
left 是建筑物的左边界坐标。 right 是建筑物的右边界坐标。 height 是建筑物的高度。 天际线 应该表示为由 “关键点” 组成的列表,格式 [[x1,y1],[x2,y2],...] ,并按 x 坐标 进行 排序 。关键点是水平线段的左端点。列表中最后一个点是最右侧建筑物的终点,y 坐标始终为 0 ,仅用于标记天际线的终点。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。
注意:输出天际线中不得有连续的相同高度的水平线。例如 [...[2 3], [4 5], [7 5], [11 5], [12 7]...] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...]
解题思路:
解决天际线问题的一种常见方法是使用扫描线算法,结合优先队列(最大堆)来处理建筑物的边界和高度变化。以下是详细的解题步骤:
-
提取关键点:
- 将每个建筑物的左边界和右边界分别提取出来,左边界标记为进入事件,右边界标记为离开事件。
- 对于左边界
(left, height)
,标记为[left, -height]
(负号用于区分进入和离开事件)。 - 对于右边界
(right, height)
,标记为[right, height]
。
-
排序关键点:
- 将所有关键点按照 x 坐标进行排序。如果 x 坐标相同,则按照 y 坐标排序,确保进入事件在离开事件之前处理。
-
扫描线处理:
- 使用一个最大堆(优先队列)来维护当前扫描线上的建筑物高度。
- 遍历排序后的关键点,根据事件类型(进入或离开)更新堆。
- 每次堆顶元素变化时,记录当前 x 坐标和堆顶高度作为天际线的一个关键点。
-
生成结果:
- 将记录的关键点按顺序输出,即为所求的天际线。
代码实现(Python):
import heapq
def getSkyline(buildings):
提取关键点
events = []
for left, right, height in buildings:
events.append((left, -height)) # 进入事件
events.append((right, height)) # 离开事件
# 排序关键点
events.sort()
# 最大堆,存储 (高度, 右边界)
max_heap = [(0, float('inf'))] # 初始地面高度为0,右边界无限远
prev_height = 0
skyline = []
# 扫描线处理
for x, h in events:
if h < 0: # 进入事件
heapq.heappush(max_heap, (h, x))
else: # 离开事件
max_heap.remove((-h, x))
heapq.heapify(max_heap)
# 当前最高建筑的高度
current_height = -max_heap[0][0]
if current_height != prev_height:
skyline.append([x, current_height])
prev_height = current_height
return skyline
示例
buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]] print(getSkyline(buildings))
输出解释:
对于示例输入 [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
,输出应为 [[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]]
,表示天际线的各个关键点。
复杂度分析:
- 时间复杂度:
O(N log N)
,其中 N 是建筑物的数量。主要耗时在排序和堆操作上。 - 空间复杂度:
O(N)
,用于存储关键点和堆。
通过上述步骤和代码实现,可以有效地解决 The Skyline Problem。希望这个详细的解答对你有所帮助!如果有任何进一步的问题,欢迎继续提问。
发表回复