姊妹篇
leetcode 223. 矩形面积 - 850. 矩形面积 II - 坐标投影 -线段覆盖 - 有效区间计算 -pow 指数计算 -含默认值的字典-有序列表-counter
城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度,请返回由这些建筑物形成的 天际线 。
每个建筑物的几何信息由数组 buildings 表示,其中三元组 buildings[i] = [lefti, righti, heighti] 表示:
lefti 是第 i 座建筑物左边缘的 x 坐标。
righti 是第 i 座建筑物右边缘的 x 坐标。
heighti 是第 i 座建筑物的高度。
天际线 应该表示为由 “关键点” 组成的列表,格式 [[x1,y1],[x2,y2],…] ,并按 x 坐标 进行 排序 。关键点是水平线段的左端点。列表中最后一个点是最右侧建筑物的终点,y 坐标始终为 0 ,仅用于标记天际线的终点。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。
注意:输出天际线中不得有连续的相同高度的水平线。例如 […[2 3], [4 5], [7 5], [11 5], [12 7]…] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[…[2 3], [4 5], [12 7], …]
示例 1:
输入: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]]
解释:
图 A 显示输入的所有建筑物的位置和高度,
图 B 显示由这些建筑物形成的天际线。图 B 中的红点表示输出列表中的关键点。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-skyline-problem
思路1 坐标投影,找每一个坐标点的最大y - 超时 - 修改后通过
感谢评论区给出的修正后的代码,此方法得以通过
主要是用到了下标映射,减少了查询时间
在这篇文章代码基础上稍微修改即可
leetcode 223. 矩形面积 - 850. 矩形面积 II - 坐标投影 -线段覆盖 - 有效区间计算 -pow 指数计算 -含默认值的字典-有序列表-counter
class Solution:
def getSkyline(self, buildings):
ans = []
'''
映射矩形的x坐标到坐标轴
'''
buildings.sort()
xs=[]
for x1, x2, y2 in buildings:
xs.append(x1)
xs.append(x2)
xs = list(set(xs))
xs.sort()
'''
找x对应的 max y
'''
cy = {}
for x1, x2, y2 in buildings:
for i in range(len(xs)):
if i not in cy: cy[i] = 0
if x1<=xs[i]:
if x2>xs[i]:
cy[i] = max(cy[i],y2)
else:
break
'''
求坐标
'''
i = 0
ans.append([xs[i],cy[i]])
for i in range(1,len(xs)-1):
if cy[i]!=cy[i-1]:
ans.append([xs[i],cy[i]])
i+=1
ans.append([xs[i], cy[i]])
return ans
思路2 线段树 - 超时
tips:
- 每个结点包含了一个区间 a,b 闭区间
- mid = (a+b)//2
- 左孩子是 [a,mid]
- 右孩子是[mid+1,b]
- 节点中再存一个最大的y,来一个建筑,就更新这个建筑区间内所有节点的y
- 天际线和建筑的右边际无关,需要把开区间变成闭区间,右边际-1即可
class Solution:
def getSkyline(self, buildings):
ans = []
tp = []
'''
映射矩形的x坐标到坐标轴
'''
xs = list({x for xy in buildings for x in xy[:2]})
xs.sort()
class TreeNode():
def __init__(self,l,r):
self.l = l
self.r = r
self.cl = None
self.cr = None
self.v = 0
'''
生成线段树
'''
def dfs(a,b):
node = TreeNode(a, b)
if a== b: return node
mid = (a+b)//2
node.cl = dfs(a,mid)
node.cr = dfs(mid+1,b)
return node
'''
查找有效节点
'''
def find(node,x1,x2,y,search=False):
if not node or x2<x1:return
if x1>node.r or x2<node.l:return
if x1<=node.l<=node.r<=x2:
node.v = max(node.v, y)
if search and x1==x2:
if tp and node.v == tp[-1][1]:
return
tp.append([x1,node.v])
return
l = max(node.l,x1)
r = min(node.r,x2)
y = max(node.v, y)
find(node.cl,l,r,y,search)
find(node.cr,l,r,y,search)
'''
查找线段树,并更新节点
'''
node = dfs(xs[0],xs[-1])
for x1, x2, y in buildings:
find(node,x1,x2-1,y)
'''
更新列表
'''
for i in range(len(xs)):
find(node, xs[i], xs[i], 0, True)
ans.extend(tp)
return ans
思路2 优化 离散化x - 通过
优化前,线段树的根节点区间和x的大小值有关
优化后,线段树的根节点区间和x的个数有关
区间变小,树的深度就会变浅,从而得到优化
'''
线段树+坐标离散
'''
class Solution:
def getSkyline(self, buildings):
ans = []
tp = []
'''
映射矩形的x坐标到坐标轴
'''
xs = list({x for xy in buildings for x in xy[:2]})
xs.sort()
p2i = {x: i for i, x in enumerate(xs)}
class TreeNode():
def __init__(self, l, r):
self.l = l
self.r = r
self.cl = None
self.cr = None
self.v = 0
'''
生成线段树
'''
def dfs(a, b):
node = TreeNode(a, b)
if a == b: return node
mid = (a + b) // 2
node.cl = dfs(a, mid)
node.cr = dfs(mid + 1, b)
return node
'''
查找有效节点
'''
def find(node, x1, x2, y, search=False):
if not node or x2 < x1: return
if x1 > node.r or x2 < node.l: return
if x1 <= node.l <= node.r <= x2:
node.v = max(node.v, y)
if search and x1 == x2:
if tp and node.v == tp[-1][1]:
return
tp.append([xs[x1], node.v])
return
l = max(node.l, x1)
r = min(node.r, x2)
y = max(node.v, y)
find(node.cl, l, r, y, search)
find(node.cr, l, r, y, search)
'''
查找线段树,并更新节点
'''
node = dfs(0, len(xs)-1)
for x1, x2, y in buildings:
find(node, p2i[x1], p2i[x2]-1, y)
'''
更新列表
'''
for i in range(len(xs)):
find(node, i, i, 0, True)
ans.extend(tp)
return ans
执行效果