LintCode 131 [Building Outline]

原題

水平面上有 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,018評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,417評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,960評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,267評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,471評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,698評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,204評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,436評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)。 張土汪:刷leetcod...
    土汪閱讀 12,764評(píng)論 0 33
  • 原文發(fā)表在我的博客:大樓輪廓實(shí)現(xiàn)及優(yōu)化求關(guān)注、求交流、求意見(jiàn)、求建議。 問(wèn)題 LintCode:大樓輪廓 描述 水...
    華方閱讀 1,881評(píng)論 0 8
  • http://www.sobaidupan.comhttp://www.dajudeng.com
    歷奇閱讀 229評(píng)論 0 0
  • 溜了溜了 這四個(gè)字很容易讓人聯(lián)想到一個(gè)個(gè)奔跑到模糊的背影和來(lái)自旁人的驚愕表情。 溜走了,趁別人不注意,也趁自己...
    僧聞閱讀 380評(píng)論 0 5
  • 三月,艷陽(yáng)高照 流離失所的,是 海子的詩(shī)魂 飄蕩,輕揚(yáng) 纏綿在指間 一段揉碎的陽(yáng)光 支離破碎的山海關(guān) 艷陽(yáng)高照,卻...
    飲一杯苦酒閱讀 263評(píng)論 0 1