The Skyline Problem
要点:这题是难题,但是是重点难题,因为google常考,而且很多变形。题本身的pattern就是从左到右更新区间内存在值的最大值(对于这题就是skyline)。最简单的解法就是从左到右根据每个区间的enter/exit point更新存在值的集合。 同时因为区间值集合是不断变化的,需要有个data structure来track当前最大值,priority queue和tree map都可以。
实现上还有不少细节
- priority queue中元素类型?一个integer就够了
- compare function:等于?
- 注意这个compare function是对时间点sort时候用的,而不是priority queue的,priority queue只根据height来排序
- 输入输出
EDIT: 补充更好的方法(更简单,并且beat 98.2% python)
- 因为building已经按start排好了,不用再排序。heap的最大height就是当前iteration的skyline。当然可能当前的height和当前skyline相同,不更新,这个code的结构就非常简单。
- 如何定义当前iteration?每个新的start和当前在heap的最大height的end做比较,两个分支:
- 新的start的存在并且<=heap顶的end,这时候push(push要在loop里把所有start的都push进去,heap顶end也要push)
- 1的reverse:没新start了,或者新start有空隙。这时候当前iteration就是考虑当前skyline的end(也就会上轮heap的最大值)。pop也是在loop里,把所有已经结束的段pop出来。
- x: either next start or current end, so it’s in skyline
- 综上,总循环就是i<n or heap有值
- 边界条件:
- 如果start和heap顶end相同,那么push,因为这样height是连续下去的
- push的时候,多个start相同,要全部push,因为以最大的height为准
- pop的时候要pop所有dead end:所以第二维也要按最大排序,这样height相同的时候保证end大的作为标准
- lintcode的输出方式:
- 不是只需要跳变,而是需要输出非0区间
- 区间:记录last height,而height变化的时候就输出,只要不是变成0,上一个end和下一个start是同一点,一起更新
- 非0:如果是0,那么start是不更新的,reset为-1。同理,如果start是-1,下一次跳变更新start
class Solution(object):
def getSkyline(self, buildings):
"""
:type buildings: List[List[int]]
:rtype: List[List[int]]
"""
LRH = buildings
i, n = 0, len(buildings)
liveHR = []
skyline = []
while i<n or liveHR:
if not liveHR or i<n and LRH[i][0]<=-liveHR[0][1]: # prefer push than pop when ==
x = LRH[i][0]
while i<n and x==LRH[i][0]:
heapq.heappush(liveHR, (-LRH[i][2], -LRH[i][1]))
i+=1
else: # time to pop as next start is greater or no next
x = -liveHR[0][1]
while liveHR and -liveHR[0][1]<=x: # error: not ==x, <=x, other dead ones are not popped
heapq.heappop(liveHR)
height = len(liveHR) and -liveHR[0][0] # error: not or, and
if not skyline or skyline[-1][1]!=height:
skyline.append((x, height)) # x is always valid, either next start or current end
return skyline