第六章 優先隊列(堆) 閱讀總結

6. 1 模型

????????優先隊列是允許至少下列兩種操作的數據結構: insert(插入),它的作用是顯而易見的;以及deleteMin(刪除最小者), 它的工作是找出、返回并刪除優先隊列中最小的元素。insert操作等價于enqueue(入隊),而deleteMin則是隊列運算dequeue(出隊)在優先隊列中的等價操作。


????????如同大多數數據結構那樣,有時可能要添加一些其他的操作,但這些添加的操作屬于擴展的操作,而不是圖6-1所描述的基本模型的一部分。

6.2 一些簡單的實現

????????有幾種明顯的方法可用于實現優先隊列。我們可以使用一個簡單鏈表在表頭以0(1)執行插入操作,并遍歷該鏈表以刪除最小元,這又需要O(N)時間。另一種方法是始終讓鏈表保持排序狀態;這使得插入代價高昂(O(N)) 而deleteMin花費低廉(0(1))。基于deleteMin的操作從不多于插入操作的事實,前者恐怕是更好的想法。

????????另一種實現優先隊列的方法是使用二叉查找樹,它對這兩種操作的平均運行時間都是O(log N)。盡管插入是隨機的,而刪除則不是,但這個結論還是成立的。記住我們刪除的唯一元素是最小元。 反復除去左子樹中的節點似乎會損害樹的平衡,使得右子樹加重。 然而,右子樹是隨機的。 在最壞的情形下,即deleteMin將左子樹刪空的情形下,右子樹擁有的元素最多也就是它應具有的兩倍。 這只是在期望的深度上加了 個小常數。 注意,通過使用 棵平衡樹,可以把這個界變成最壞情形的界;這將防止出現壞的插入序列。

????????使用查找樹可能有些過分,因為它支持許許多多并不需要的操作。 我們將要使用的基本的數據結構不需要鏈,它以最壞情形時間0(logN)支持上述兩種操作。 插入操作實際上將花費常數平均時間,若尤刪除操作的干擾,該結構的實現將以線性時間建立 個具有N項的優先隊列。

6.3二叉堆

????????我們將要使用的這種工具叫作二叉堆(binary heap) , 它的使用對于優先隊列的實現相當普遍,以至于當堆(heap)這個詞不加修飾地用在優先隊列的上下文中時,一般都是指數據結構的這種實現。在本節,我們把二叉堆只叫作堆。像二叉查找樹一樣,堆也有兩個性質,即結構性和堆序性。恰似AVL樹,對堆的一次操作可能破壞這兩個性質中的一個,因此,堆的操作必須到堆的所有性質都被滿足時才能終止。事實上這并不難做到。

6.3. 1 結構性質

? ??????堆是一棵被完全填滿的二叉樹,有可能的例外是在底層,底層上的元素從左到右填入。這樣的樹稱為完全二叉樹(complete binarytree)。圖6-2給出了一個例子。


????????完全二叉樹的高是 log N,顯然它是0(logN)。

????????一個重要的觀察發現,因為完全二叉樹這么有規律,所以它可以用一個數組表示而不需要使用鏈。

????????對于數組中任一位置i上的元素,其左兒子在位置2i上,右兒子在左兒子后的單元(2i +1)中,它的父親則在位置[i/2]上。因此,這里不僅不需要鏈,而且遍歷該樹所需要的操作極簡單,在大部分計算機上運行很可能非常快。這種實現方法的唯一問題在于,最大的堆大小需要事先估計,但一般這并不成問題(而且如果需要, 我們可以重新調整大小)。在圖6-3中,堆大小的限界是13個元素。該數組有一個位置0, 后面將詳細敘述。

????????因此, 一個堆結構將由一個(Comparable對象的)數組和一個代表當前堆的大小的整數組成。圖6-4顯示一個優先隊列的架構。

????????本章我們將始終把堆畫成樹,這意味著具體的實現將使用簡單的數組。

6.3.2 堆序性質

????????讓操作快速執行的性質是堆序性質(heap-order property)。由于我們想要快速找出最小元,因此最小元應該在根上。如果我們考慮任意子樹也應該是一個堆,那么任意節點就應該小于他的所有后裔。

????????應用這個邏輯,我們得到堆序性質。在一個堆中,對于每一個節點X,X 的父親中的關鍵字小于(或等于)X 中的關鍵字,根節點除外(它沒有父親)。

https://gitee.com/sunyunjie/DataStrycturesAndAlgorithmAnalysis

6.3.3 基本的堆操作

insert(插入)


????????為將一個元素X插入到堆中,我們在下一個可用位置創建一個空穴,否則該堆將不是完全樹。如果X可以放在該空穴中而并不破壞堆的序,那么插入完成。 否則,我們把空穴的父節點上的元 素移入該空穴中,這樣,空穴就朝著根的方向上冒一步。 繼續該過程直到X能被放入空穴中為止。如圖6-6所示,為了插入14, 我們在堆的下一個可用位置建立一個空穴。 由于將14插入空穴破壞了堆序性質,因此將31移入該空穴。在圖6-7中繼續這種策略,直到找出置入14的正確位置。


????????如果欲插入的元素是新的最小元從而一直上濾到根處,那么這種插入的時間將長達O(logN)。 平均看來,上濾終止得要早;

? ??????deleteMin(刪除最小元)

? ??????deleteMin以類似于插入的方式處理。找出最小元是容易的,困難之處是刪除它。當刪除一個最小元時,要在根節點建立一個空穴。由于現在堆少了一個元素,因此堆中最后一個元素X 必須移動到該堆的某個地方。如果X可以被放到空穴中,那么dele七eMin完成。不過這一般不太可能,因此我們將空穴的兩個兒子中較小者移入空穴,這樣就把空穴向下推了一層。重復該步驟直到X可以被放入空穴中。因此,我們的做法是將X置入沿著從根開始包含最小兒子的一條路徑上的一個正確的位置。

????????圖6-9中左圖顯示了deleteMin之前的堆。刪除13后,我們必須試圖正確地將31放到堆中。31 不能放在空穴中,因為這將破壞堆序性質。于是,我們把較小的兒子14置入空穴,同時空穴下滑一層(見圖6-10)。重復該過程,山于31大于19, 因此把19置入空穴,在更下一層上建立一個新的空穴。然后,由于31還是太大, 因此再把26置入空穴,在底層又建立一個新的空穴。最后, 我們得以將31置入空穴中(圖6-11)。這種一般的策略叫作下濾(percolate down)。在其實現例程中我們使用類似于在insert例程中用過的技巧來避免進行交換操作。

????????這種操作最壞情形運行時間為 O(log N)。平均而言,被放到根處的元素幾乎下濾到堆的底層(即它所來自的那層),因此平均運行時間為 O(log N) 。

6. 5 d-堆

? ??????二叉堆是如此簡單,以至于它們幾乎總是用在需要優先隊列的時候。d-堆是二叉堆的簡單推廣,它就像一個二叉堆,只是所有的節點都有d個兒子(因此,二叉堆是2-堆)。


????????圖 6-19 表示的是一個 3-堆。注意,d-堆要比二叉堆淺得多,它將 insert操作的運行時間 改進為 O(logd N)。然而,對于大的 d, deleteMin 操作費時得多,因為雖然樹是淺了,但是 d個兒子中的最小者是必須要找出的,如使用標準的算法,這會花費d-1次比較,于是將操作的用時提高到 O(d logd N)。如果 d 是常數,那么當然兩個的運行時間都是 O(log N)。雖然仍然可以使用一個數組,但是,現在找出兒子和父親的乘法和除法都有個因子 d, 除非 d 是 2 的幕,否則將會大大增加運行時間,因為我們不能再通過移一個二進制位來實現除法了。d-堆在理論上很有趣,因為存在許多算法,其插入次數比 dele匡Min 的次數多得多(因此理論上的加速是可能的)。當優先隊列太大而不能完全裝入主存的時候,d-堆也是很有用的。在這種情況下,d-堆 能夠以與B樹大致相同的方式發揮作用。最后,有證據顯示,在實踐中4-堆可以勝過二叉堆。

6.6 左式堆

? ??????設計一種堆結構像二叉堆那樣有效地支持合并操作(即以o(N)時間處理一個merge)而且 只使用一個數組似乎很困難。 原因在于,合并似乎需要把一個數組拷貝到另一個數組中去,對于相同大小的堆這將花費時間O(N)。 正因為如此,所有支持有效合并的高級數據結構都需要使用鏈式數據結構。

6. 6. 1 左式堆性質

????????我們把任一節點X的零路徑長(null pathleng th) npl (X)定義為從X到一個不具有兩個兒子的節點的最短路徑的長。因此,具有0個或一個兒子的節點的npl 為o, 而npl(null)= -1。在圖6-20的樹中, 零路徑長標記在樹的節點內。

????????注意,任一節點的零路徑長比它的各個兒子節點的零路徑長的最小值大l。這個結論也適用少于兩個兒子的節點,因為null的零路徑長是-1。

????????左式堆性質是:對于堆中的每一個節點X, 左兒子的零路徑長至少與右兒子的零路徑長相等。圖6-20中只有一棵樹,即左邊的那棵樹滿足該性質。這個性質實際上超出了它確保樹不平衡的要求,因為它顯然偏重于使樹向左增加深度。確實有可能存在由左節點形成的長路徑構成的樹(而且實際上更便于合并操作) 因此,我們就有了名稱左式堆(leftist heap)。

????????因為左式堆趨向于加深左路徑,所以右路徑應該短。事實上,沿左式堆右側的右路徑確實是該堆中最短的路徑。否則,就會存在過某個節點X的一條路徑通過它的左兒子,此時X 就破壞了左式堆的性質。

定理6.2 在右路徑上有r個節點的左式樹必然至少有2^r-1個節點。

6.6.2 左式堆操作

????????對左式堆的基本操作是合并。 注意,插入只是合并的特殊情形,因為我們可以把插入看成 是單節點堆與一個大的堆的merge。 首先,我們給出一個簡單的遞歸解法,然后介紹如何能夠非遞歸地執行該解法。 我們的輸入是兩個左式堆H1和H2, 見圖6-21。 讀者應該驗證,這些堆確實是左式堆。 注意, (最小的元素在根處。 除數據、左引用和右引用所用空間外,每個節點還要有一個指示零路徑長的項。

????????如果這兩個堆中有 個堆是空的,那么我們可以返回另外一個堆。 否則,合并這兩個堆, 比較它們的根。 首先,我們遞歸地將具有大的根值的堆與具有小的根值的堆的右子堆合并,, 在本例中,我們遞歸地將H2與h1的根在8處的右子堆合并, (18得到圖6-22中的堆。

? ??????由于這棵樹是遞歸形成的,而我們尚未 對算法描述完畢,因此,現在還不能說明該是如何得到的。 不過,有理由假設,最后 的結果是一個左式堆,因為它是通過遞歸的步驟得到的。 這很像歸納法證明中的歸納 圖6-22 將H2 與凡的右子堆合并的結果假設。 既然我們能夠處理基準情形(發生在一棵樹是空的時候),當然可以假設,只要能夠完成合并那么遞歸步驟就是成立的,這是遞歸

6.7 斜堆

????????斜堆(skew heap)是左式堆的自調節形式,實現起來極其簡單。 斜堆和左式堆間的關系類似于伸展樹和AVL樹間的關系。 斜堆是具有堆序的二叉樹,但不存在對樹的結構限制。 不同于左 式堆,關于任意節點的零路徑長的任何信息都不再保留。 斜堆的右路徑在任何時刻都可以任意長,因此,所有操作的最壞情形運行時間均為O(N)。 然而,正如伸展樹一樣,可以證明(見第11章)對任意M次連續操作,總的最壞情形運行時間是O(Mlog N)。 因此,斜堆每次操作的攤還開銷 (amortized cost) 為 O(logN)。

? ??????與左式堆相同,斜堆的基本操作也是合并操作。merge例程還是遞歸的,我們執行與以前完全相同的操作,但有一個例外,即:對于左式堆,我們查看是否左兒子和右兒子滿足左式堆結構性質, 并在不滿足該性質時將它們交換。但對于斜堆,交換是無條件的,除那些右路徑上所有節點的最大者不交換它的左右兒子的例外外, 我們都要進行這種父換。這個例外就是在自然遞歸實現時所發生的情況,因此它實際上根本不是特殊情形。此外,證明時間界也是不必要的,但是,由于這樣的節點肯定沒有右兒子,因此執行交換是不明智的(在我們的例子中,該節點沒有兒子,因此我們不必為此擔心)。另外,仍設我們的輸入是與前面相同的兩個堆,見圖6-31如果我們遞歸地將H2與H1

的根在8處的子堆合并,那么將得到圖6-32中的堆。

?????????注意,因為右路徑可能很長,所以遞歸實現可能由于缺乏棧空間而失敗,盡管在其他方面性能是可接受的。 斜堆有一個優點,即不需要附加的空間保留路徑長以及不需要測試以確定何時交換兒子。 精確確定左式堆和斜堆的右路徑長的期望值是一 個尚未解決的問題(后者無疑更為困難)。 這樣的比較將更容易確定平衡信息的輕微遺失是否可由缺乏測試來補償。

6.8 二項隊列


????????雖然左式堆和斜堆都在每次操作以O(logN)時間有效地支持合并、插入和deleteMin,但還是有改進的余地,因為我們知道,二叉堆以每次操作花費常數平均時間支持插入。二項隊列支持所有這三種操作,每次操作的最壞情形運行時間為0(logN) , 而插入操作平均花費常數時間。

6. 8. 1 二項隊列結構

????????二項隊列(binomial queue)與我們已經看到的所有優先隊列的實現的區別在于,一個二項隊 列不是一棵堆序的樹,而是堆序的樹的集合,稱為森林(forest)。每一棵堆序樹都是 有約束的形式,叫作二項樹(binomial tree , 后面將看到該名稱的由來是顯然的)。每一個高度上至多存在一棵二項樹。高度為0的二項樹是一棵單節點樹;高度為k的二項樹凡通過將一棵二項樹 Bk -I附接到 另 一棵二項樹 Bk-I的根上而構成。圖6-34顯示二項樹 B0,B1,B2,B3以及B4.

????????從圖中看到,二項樹隊由一個帶有兒子B0, B1,。。。,BK-1的根組成。高度為k的二項樹恰好有2k個節點,而在深度d 處的節點數是二項系數(d)。如果我們把堆序施加到二項樹上并允許任意高度上最多一棵二項樹,那么就能夠用二項樹的集合表示任意大小的優先隊列。例如,大小為13 的優先隊列可以用森林B3, B2, B。表示。我們可以把這種表示寫成1101, 它不僅以二進制表示了13'而且也表示這樣的事實:在上述表示中,B3, B2, B。出現,而B1則沒有。

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

推薦閱讀更多精彩內容

  • 0.目錄 1.優先隊列ADT 2.幾種實現 3.二叉堆 4.d-堆 5.左式堆 6.斜堆 7.二項隊列 8.斐波那...
    王偵閱讀 3,134評論 1 2
  • 1 序 2016年6月25日夜,帝都,天下著大雨,拖著行李箱和同學在校門口照了最后一張合照,搬離寢室打車去了提前租...
    RichardJieChen閱讀 5,135評論 0 12
  • 一些概念 數據結構就是研究數據的邏輯結構和物理結構以及它們之間相互關系,并對這種結構定義相應的運算,而且確保經過這...
    Winterfell_Z閱讀 5,886評論 0 13
  • 樹的概述 樹是一種非常常用的數據結構,樹與前面介紹的線性表,棧,隊列等線性結構不同,樹是一種非線性結構 1.樹的定...
    Jack921閱讀 4,475評論 1 31
  • 1.期中考卷付印 2.講評單元練習卷 3.布置周末考前作業 4.聽關于大腦潛能開發的講座 5...
    田米米閱讀 321評論 0 0