原題
水平面上有 N 座大樓,每座大樓都是矩陣的形狀,可以用三個(gè)數(shù)字表示 (start, end, height),分別代表其在x軸上的起點(diǎn),終點(diǎn)和高度。大樓之間從遠(yuǎn)處看可能會(huì)重疊,求出N 座大樓的外輪廓線。
外輪廓線的表示方法為若干三元組,每個(gè)三元組包含三個(gè)數(shù)字 (start, end, height),代表這段輪廓的起始位置,終止位置和高度。
樣例
給出三座大樓:
[
[1, 3, 3],
[2, 4, 4],
[5, 6, 1]
]
外輪廓線為:
[
[1, 2, 3],
[2, 4, 4],
[5, 6, 1]
]
解題思路
- Sweep-Line - 掃描線問(wèn)題,可以每次移動(dòng)一個(gè)非常小的單位,不斷去求當(dāng)下樓房的高度。但實(shí)際上,只有某個(gè)樓房的開始或結(jié)尾才有交點(diǎn)的變化。
- 第一步,拆分(1, 3, 3) => (1, 3, 起點(diǎn)) 和 (3, 3, 終點(diǎn)),然后排序,關(guān)于排序,對(duì)于含有坐標(biāo),高度,始末標(biāo)記的三元組
- 首先按坐標(biāo)排序(大小排序)
- 坐標(biāo)一樣時(shí),按高度排序(大小排序)
- 高度也一樣時(shí),按始末排序,始 > 末
- 排序非常重要, 時(shí)間復(fù)雜度是O(nlogn)
- 每次到交點(diǎn)的時(shí)候,要求幾個(gè)樓房的高度最大值 - Heap
- 每次到某一個(gè)樓房終點(diǎn)的時(shí)候要從堆中刪除相應(yīng)的高度 - HashHeap 時(shí)間復(fù)雜度是O(nlogn)
- 時(shí)間復(fù)雜度 O(nlogn) + O(nlogn) => O(nlogn)
- 如何查看堆頂元素
myheap.queue[0]
完整代碼
class node:
"""
create a node class to handle the duplicates in heap.
id => the index of x, num = number of x in heap
"""
def __init__(self, id, number):
self.id = id
self.num = number
class HashHeap:
def __init__(self):
self.map = {}
self.hashmaxheap = [0]
self.map[0] = node(0, 1)
self.currentSize = 0
def put(self, data):
"""add a new item to the hashmaxheap"""
if data in self.map:
existData = self.map[data]
self.map[data] = node(existData.id, existData.num + 1)
self.currentSize += 1
return
else:
self.hashmaxheap.append(data)
self.map[data] = node(len(self.hashmaxheap) - 1, 1)
self.currentSize += 1
self.siftUp(len(self.hashmaxheap) - 1)
def peek(self):
"""returns the item with the maxmum key value"""
return self.hashmaxheap[1]
def get(self):
"""returns the item with the maxmum key value, removing the item from the heap"""
res = self.hashmaxheap[1]
if self.map[res].num == 1:
if self.map[res].id == len(self.hashmaxheap) - 1:
del self.map[res]
self.hashmaxheap.pop()
self.currentSize -= 1
return res
del self.map[res]
self.hashmaxheap[1] = self.hashmaxheap[-1]
self.map[self.hashmaxheap[1]] = node(1, self.map[self.hashmaxheap[1]].num)
self.hashmaxheap.pop()
self.siftDown(1)
else:
self.map[res] = node(1, self.map[res].num - 1)
self.currentSize -= 1
return res
def delete(self, data):
existData = self.map[data]
if existData.num == 1:
del self.map[data]
if existData.id == len(self.hashmaxheap) - 1:
self.hashmaxheap.pop()
self.currentSize -= 1
return
self.hashmaxheap[existData.id] = self.hashmaxheap[-1]
self.map[self.hashmaxheap[-1]] = node(existData.id, self.map[self.hashmaxheap[-1]].num)
self.hashmaxheap.pop()
self.siftUp(existData.id)
self.siftDown(existData.id)
else:
self.map[data] = node(existData.id, existData.num - 1)
self.currentSize -= 1
def siftUp(self, index):
# // means devide by 2 and return int
while index // 2 > 0:
if self.hashmaxheap[index] < self.hashmaxheap[index // 2]:
break
else:
numA = self.map[self.hashmaxheap[index]].num
numB = self.map[self.hashmaxheap[index // 2]].num
self.map[self.hashmaxheap[index]] = node(index // 2, numA)
self.map[self.hashmaxheap[index // 2]] = node(index, numB)
self.hashmaxheap[index], self.hashmaxheap[index // 2] = self.hashmaxheap[index // 2], self.hashmaxheap[index]
index = index // 2
def siftDown(self, index):
"""correct single violation in a sub-tree"""
if index > (len(self.hashmaxheap) - 1) // 2:
return
# find the max child of current index
if (index * 2 + 1) > (len(self.hashmaxheap) - 1) or self.hashmaxheap[index * 2] > self.hashmaxheap[index * 2 + 1]:
maxChild = index * 2
else:
maxChild = index * 2 + 1
if self.hashmaxheap[index] > self.hashmaxheap[maxChild]:
return
else:
numA = self.map[self.hashmaxheap[index]].num
numB = self.map[self.hashmaxheap[maxChild]].num
self.map[self.hashmaxheap[index]] = node(maxChild, numA)
self.map[self.hashmaxheap[maxChild]] = node(index, numB)
self.hashmaxheap[index], self.hashmaxheap[maxChild] = self.hashmaxheap[maxChild], self.hashmaxheap[index]
self.siftDown(index * 2)
self.siftDown(index * 2 + 1)
def size(self):
return self.currentSize
def isEmpty(self):
return self.currentSize == 0
def sorter(x, y):
if x[0] != y[0]:
return x[0] - y[0]
elif x[1] != y[1]:
return x[1] - y[1]
# 相同時(shí)間點(diǎn)上,加入新樓有優(yōu)先權(quán)
return y[2] - x[2]
class Solution:
# @param buildings: A list of lists of integers
# @return: A list of lists of integers
def buildingOutline(self, buildings):
# write your code here
if len(buildings) == 0:
return []
timepoints = [] # pos, height, in/out
for building in buildings:
timepoints.append((building[0], building[2], 1))
timepoints.append((building[1], building[2], -1))
timepoints = sorted(timepoints, cmp=sorter)
heights = HashHeap()
left = 1
res = []
for timepoint in timepoints:
if timepoint[2] == 1:
if heights.isEmpty() or timepoint[1] > heights.peek():
if timepoint[0] != left and not heights.isEmpty():
res.append([left, timepoint[0], heights.peek()])
left = timepoint[0]
heights.put(timepoint[1])
else:
heights.delete(timepoint[1])
if heights.isEmpty() or timepoint[1] > heights.peek():
res.append([left, timepoint[0], timepoint[1]])
left = timepoint[0]
return res