📅  最后修改于: 2023-12-03 14:51:42.953000             🧑  作者: Mango
天际线问题(Skyline Problem)是指给定一系列建筑物的轮廓线,求解出从远处观察时,能够看到的建筑物轮廓线。这个问题可以用于城市规划,建筑物设计等领域。
一种常见的解法是使用扫描线算法。我们将建筑物按照从左到右的顺序排序,在扫描线向右移动的过程中,记录下当前能够看到的所有建筑物的最高高度。如果有建筑物的起点在当前扫描线的右侧,这个建筑物就会成为可见的建筑物,并将其高度加入到一个优先队列中。如果有建筑物的终点在当前扫描线的右侧,这个建筑物就会离开可见建筑物的集合。如果有建筑物比当前最高的建筑物更高,那么这个建筑物将成为当前可见建筑物中的一员,而原本能够看到的较低的建筑物就会被遮挡。
from heapq import *
from typing import List
def getSkyline(buildings: List[List[int]]) -> List[List[int]]:
# 初始化扫描线
events = [(L, -H, R) for L, R, H in buildings]
events += list({(R, 0, 0) for _, R, _ in buildings})
events.sort()
# 初始化优先队列
pq = [(0, float('inf'))]
res = []
for x, h, r in events:
while x >= pq[0][1]:
heappop(pq)
if h:
heappush(pq, (h, r))
if res[-1][1] != -pq[0][0]:
res.append([x, -pq[0][0]])
return res[1:]
另一种解法是使用线段树。我们将建筑物按照从左到右的顺序排序,并将每个建筑物的起始位置和终止位置分别作为区间的左端点和右端点,区间高度设为建筑物高度。在进行线段树的构建和查询时,我们使用区间最大值作为区间高度,在向上递归时,我们需要同时维护区间的最大值和最小值。如果区间的最大值等于当前扫描线的高度,那么这个区间就是可见的。
from typing import List
class SegmentTreeNode:
def __init__(self, l: int, r: int):
self.l, self.r = l, r
self.max_height = 0
self.min_height = 0
self.left = None
self.right = None
class Solution:
def getSkyline(self, buildings: List[List[int]]) -> List[List[int]]:
n = len(buildings)
if n == 0:
return []
xs = sorted(list(set([L for L, _, _ in buildings] + [R for _, R, _ in buildings])))
x_dict = {xs[i]: i for i in range(len(xs))}
root = self.buildSegmentTree(0, len(xs) - 1, x_dict)
for L, R, H in buildings:
self.modifySegmentTree(root, x_dict[L], x_dict[R] - 1, H)
res = []
for i in range(len(xs) - 1):
max_height = self.querySegmentTree(root, i)
if len(res) == 0 or max_height != res[-1][1]:
res.append([xs[i], max_height])
return res
def buildSegmentTree(self, l: int, r: int, x_dict: dict) -> SegmentTreeNode:
root = SegmentTreeNode(l, r)
if l < r:
mid = (l + r) // 2
root.left = self.buildSegmentTree(l, mid, x_dict)
root.right = self.buildSegmentTree(mid + 1, r, x_dict)
return root
def modifySegmentTree(self, root: SegmentTreeNode, l: int, r: int, height: int):
if root.l == l and root.r == r:
root.max_height = max(height, root.max_height)
root.min_height = min(height, root.min_height)
else:
mid = (root.l + root.r) // 2
if r <= mid:
self.modifySegmentTree(root.left, l, r, height)
elif l > mid:
self.modifySegmentTree(root.right, l, r, height)
else:
self.modifySegmentTree(root.left, l, mid, height)
self.modifySegmentTree(root.right, mid + 1, r, height)
root.max_height = max(root.left.max_height, root.right.max_height)
root.min_height = min(root.left.min_height, root.right.min_height)
def querySegmentTree(self, root: SegmentTreeNode, i: int) -> int:
if root.l == root.r:
return root.max_height
mid = (root.l + root.r) // 2
if i <= mid:
return self.querySegmentTree(root.left, i)
else:
if root.left.max_height >= root.right.min_height:
return self.querySegmentTree(root.left, i)
else:
return self.querySegmentTree(root.right, i)