[LeetCode] 218. 天际线问题
题目链接 :https://leetcode-cn.com/problems/the-skyline-problem/
题目描述:
城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。现在,假设您获得了城市风光照片(图A)上显示的所有建筑物的位置和高度,请编写一个程序以输出由这些建筑物形成的天际线(图B)。
每个建筑物的几何信息用三元组 [Li,Ri,Hi] 表示,其中 Li 和 Ri 分别是第 i 座建筑物左右边缘的 x 坐标,Hi 是其高度。可以保证 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX 和 Ri - Li > 0。您可以假设所有建筑物都是在绝对平坦且高度为 0 的表面上的完美矩形。
例如,图A中所有建筑物的尺寸记录为:[ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] 。
输出是以 [ [x1,y1], [x2, y2], [x3, y3], ... ] 格式的“关键点”(图B中的红点)的列表,它们唯一地定义了天际线。关键点是水平线段的左端点。请注意,最右侧建筑物的最后一个关键点仅用于标记天际线的终点,并始终为零高度。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。
例如,图B中的天际线应该表示为:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]。
说明:
- 任何输入列表中的建筑物数量保证在 [0, 10000] 范围内。
- 输入列表已经按左 x 坐标 Li 进行升序排列。
- 输出列表必须按 x 位排序。
- 输出天际线中不得有连续的相同高度的水平线。例如 [...[2 3], [4 5], [7 5], [11 5], [12 7]...] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...]
思路:
思路一:分而治之1
两两合并,最后一起合并, 所以我们要知道两个不同建筑物如何合并, 如下图所示:
使用分而治之可以合并所有建筑物!
时间复杂度为:nlog(n)
思路二:二分法
时间复杂度:n^2log(n)
思路三:堆(参考提交代码中)
大家有不清楚的地方, 欢迎留言~
代码:
思路一:分而治之
class Solution:
def getSkyline(self, buildings):
if not buildings: return []
if len(buildings) == 1:
return [[buildings[0][0], buildings[0][2]], [buildings[0][1], 0]]
mid = len(buildings) // 2
left = self.getSkyline(buildings[:mid])
right = self.getSkyline(buildings[mid:])
return self.merge(left, right)
# 两个合并
def merge(self, left, right):
# 记录目前左右建筑物的高度
lheight = rheight = 0
# 位置
l = r = 0
# 输出结果
res = []
while l < len(left) and r < len(right):
if left[l][0] < right[r][0]:
# current point
cp = [left[l][0], max(left[l][1], rheight)]
lheight = left[l][1]
l += 1
elif left[l][0] > right[r][0]:
cp = [right[r][0], max(right[r][1], lheight)]
rheight = right[r][1]
r += 1
# 相等情况
else:
cp = [left[l][0], max(left[l][1], right[r][1])]
lheight = left[l][1]
rheight = right[r][1]
l += 1
r += 1
# 和前面高度比较,不一样才加入
if len(res) == 0 or res[-1][1] != cp[1]:
res.append(cp)
# 剩余部分添加进去
res.extend(left[l:] or right[r:])
return res
思路二:二分法
class Solution:
def getSkyline(self, buildings):
import bisect
res = []
# 记录 [left, height], [right, height]
loc = []
for l, r, h in buildings:
# 为了排序让 left那边靠前, 所以让高度取负
loc.append([l, -h])
loc.append([r, h])
loc.sort()
heap = [0]
prev = 0
for x, h in loc:
if h < 0:
bisect.insort(heap, h)
else:
heap.remove(-h)
cur = -heap[0]
if prev != cur:
res.append([x, cur])
prev = cur
return res
思路三:堆
class Solution:
def getSkyline(self, buildings):
import heapq
events = sorted([(L, -H, R) for L, R, H in buildings] + list({(R, 0, 0) for _, R, _ in buildings } ))
res = [[0, 0]]
heap = [[0, float("inf")]]
for x, H, R in events:
while x >= heap[0][1]:
heapq.heappop(heap)
if H:
heapq.heappush(heap, [H, R])
if res[-1][1] != -heap[0][0]:
res.append([x, -heap[0][0]])
return res[1:]