1. 程式人生 > >第6章 優先佇列(堆)

第6章 優先佇列(堆)

優先佇列

優先佇列資料結構屬於電腦科學中最精緻的一種

6.1 模型

優先佇列允許至少兩種操作:插入和刪除最小者

6.2 一些簡單的實現

優先佇列的插入和刪除最小者的最壞情形時間均為o(logN),但插入操作實際上將花費平均時間,若無刪除操作的干擾,該結構的實現將以線性時間建立一個具有N項的優先佇列

6.3 二叉堆

堆是一棵完全二叉樹,但卻不需要鏈的存在。因為完全二叉樹的規律性,可以將堆的節點儲存在陣列中,通過父節點和子節點位置的數學關係以建立聯絡。

一個堆結構將由一個數組和一個代表當前堆的大小的整陣列成。

堆的堆序性質指父節點小於子節點,堆的結構性質指完全二叉樹結構。

堆的插入:給陣列末尾賦值,然後進行上濾操作,最壞情形花費logN的時間。

堆的刪除最小元:將陣列末尾的元素替換掉首個元素,然後進行下濾操作。

堆的其他操作:

decreaseKey,降低關鍵字的值,可以將關鍵字的值降低,然後通過上濾操作,提高該關鍵字的優先順序。

increaseKey,提高關鍵字的值,可以將關鍵字的值提高,然後通過下濾操作,降低其優先順序。

delete,可以先通過降低關鍵字的值,提高其優先順序,然後採用deleteMIn方法將其刪除

buildHeap,構建堆,先將陣列放入,再從第一個父節點開始下濾操作,只需要o(N)時間;若是逐個插入則要話費NlogN時間,因為逐個插入需要花費為對一倍的樹葉節點進行下濾操作。該操作至多需要2N-2次比較。

6.4 優先佇列的應用

選擇問題:選擇第K個大小

若對輸入排序,再取第K個,需要n的平方;若只排序k個,其他逐個比較替換,需要nk;最壞均為n平方。

若對輸入堆排序,進行K次deleteMIN,花費n+klogN;若堆排序k個,其餘逐個比較並下濾,最後取堆中最小,花費k+(n-k)logk;最壞均為nlogN。

事件模擬

銀行排隊問題

這個問題主要由兩個事件構成,顧客到達和顧客離去。

每個顧客有到達時間和服務服務時間。

排隊事件的處理進行不能採用定時掃描方法,應該讓時間直接跳到下個事件發生,然後統計並收集資料。

6.5 d-堆

當插入對於刪除最小者操作時,使用d-堆比較划算。

若插入操作為m次,刪除操作為n次,堆的叉數最佳為max(2,m/n);

6.6 左式堆

左式堆的設計是為了有效的支援合併操作,由於左式堆控制了右路徑的長度為logN以內,左式堆的合併操作只需要logN的時間複雜度。

零路徑長:任一節點的零路徑長定義為該節點到一個不具有兩個兒子節點的節點的最短路徑的長。

左式堆:對於堆中的每個節點,左兒子的零路徑長至少與右兒子的零路徑長相等。由此可推論而知,左子樹的節點數大於等於右子樹的節點數。



左式堆的合併操作:遞迴地將具有大的根植的堆與具有小的根植的堆的右子堆合併,並根據情況調整樹根的零路徑長和交換子樹。此操作既維護了堆的堆序性,又保證了合併操作的logN複雜度。

6.7 斜堆

由於不再保證對的左式性質,斜堆的單次合併操作時間複雜度為N,然而其攤還操作為logN,等同於左式堆合併複雜度。

6.8 二項佇列

由於二項佇列最多有logN棵樹,因此其合併、deleteMin等操作均為logN,對於插入的平均時間複雜度為常數。

對於二項佇列的實現,每個節點的兒子都在一個連結串列中,而且每個節點都有一個對它的第一個兒子的引用,便於快速找出根的所有子樹。二項佇列要求各個兒子按照它們的子樹的大小排序,便於合併操作的進行。

6.9 標準庫中的優先佇列

java1.5之後出現了PriorityQueue,由於優先佇列有許多實現方法,因此該類庫的設計者沒有選擇讓優先佇列成為一個介面。

疑難點

1.左式堆的懶惰刪除策略:

當執行deleteMin操作時,若堆的根節點已被作刪除標誌,則對堆進行一遍先序遍歷,將含有刪除標誌的節點刪除,並將刪除後分割成的兩個子堆依次放入佇列中。先序遍歷完成後,依次讀取兩個子堆進行合併操作,並將合併後的堆放到佇列末尾,不斷合併直到只剩一個堆為止。