leetcode 218. 天际线问题 -困难题目- 矩形覆盖 -线段树 - 坐标离散

姊妹篇
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

执行效果
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值