描述
原题在这: https://leetcode.com/problems/the-skyline-problem/
思路
分治
分为divide和merge两部分, divide比较简单, 一直对半分, 直到len(buildings) <= 1
.
对于merge阶段, 首先这个时候左侧和右侧的list都是要返回的结果的格式即[index, height]
. 现在要做的是merge为一组:
- 用两个变量
l
,r
分别表示正在处理的左侧和右侧list的下标. - 用两个变量
h1
,h2
分别表示左侧和右侧的当前高度. - 对于左侧和右侧各自的
[index, height]
, 谁的index
更小, 就在结果中放入它, 同时更新对应的下标,l++
或者r++
.(类比merge two sorted lists
) - 对于左侧和右侧各自的
[index, height]
, 如果index
相等, 说明有两个(考虑到后面尚未比较的, 可能有更多)building
都从这里起始, 那么在结果放入height
的时候,选择更大的一个height
:max(h1, h2)
, 然后同时l++
和r++
. - (接上条)
index
不同时同理, 因为l
和r
都是当前有效的高度, 那么每次往结果里面放height
, 都要放max(h1, h2)
. - 及时更新
h1
和h2
. - 在结果中加入新的值时, check一下要放入的
height
是不是和上一个height
相等, 相等的话直接跳过.
堆
首先把所有buildings
分割成两组events
并合并, 分别是[left, height, right]
表示当前building
起始于left
终止于right
, 高度是height
, 以及[right, 0, -1]
表示从right
开始高度是0
, 结束点不重要.
height
取负值, 因为排序时对于同样起始点的event
, 我们希望先处理实际高度更大的.events
排好序后开始处理, 如果发现当前的height
不是0, 说明这是起始点不是终止点. 那么这个height
在一定范围内是有效的. 把失效点right
和height
一起放入堆中. Python默认最小堆, 所以我们放入的实际是取反后的height
来模拟最大堆.- 最大堆的作用: 提供当前尚未失效的最大高度.
- 每次遇到新的坐标, 先pop出所有失效的高度(失效点早于当前点).
线段树/BIT [待完善]
假设初始时天际线是一条高度为0的水平线, 每次遇到一个building
, 就更新[left,right]
区间内的最大高度. 然后从左到右, 对每个点query一下最大高度.
线段树大概长这样:(都是闭区间)
1 | [0,7](1) |
- 用一个数组来存线段树的所有节点, 对于每个节点的下标
node
,node<<1
就是左侧子节点(表示当前范围的左侧一半),(node<<1)+1
是右侧子节点. range update(new value)
给[l, r]
代表的范围内整体赋值, 需要懒标记. 每次从外界call这个函数,入口节点下标是1
, 这个时候有效范围(1
号节点代表的)是[0,n]
, 然后在update函数中,不断二分缩小范围直到完全匹配[l,r]
.然后赋值(本题求max
), 在完全匹配后, 匹配的这个节点完成更新, 可以立即停止, 不需要继续更新下面的(所有子节点都是满足[l,r]
范围条件的, 不需要全部都更新一遍了).single point query
单点查询. 不断二分缩小范围直到当前范围长度为1,exactly match我们要找的点坐标.- 因为用了懒标记, 上层永远比下层更新更及时. 直接query到的可能没被更新过, 所以每次match到下层, 还要和上层的取
max
. - 生成结果时, query这个区间内的最大height. 因为坐标绝对值可能很大, 需要离散化.
代码
分治
1 | class Solution(object): |
堆
1 | class Solution(object): |
线段树/BIT
1 | class segTree(): |