Java資料結構與演算法解析(十三)——優先順序隊列__儲存

來源:互聯網
上載者:User
               Java資料結構與演算法解析(十三)——優先順序隊列

在很多應用中,我們通常需要按照優先順序情況對待處理對象進行處理,比如首先處理優先順序最高的對象,然後處理次高的對象。最簡單的一個例子就是,在手機上玩遊戲的時候,如果有來電,那麼系統應該優先處理打進來的電話。

在這種情況下,我們的資料結構應該提供兩個最基本的操作,一個是返回最高優先順序對象,一個是添加新的對象。這種資料結構就是優先順序隊列(Priority Queue) 。 定義

優先順序隊列和通常的棧和隊列一樣,只不過裡面的每一個元素都有一個”優先順序”,在處理的時候,首先處理優先順序最高的。如果兩個元素具有相同的優先順序,則按照他們插入到隊列中的先後順序處理。

優先順序隊列可以通過鏈表,數組,堆或者其他資料結構實現。 

優先順序隊列的實現方式 數組

最簡單的優先順序隊列可以通過有序或者無序數組來實現,當要擷取最大值的時候,對數組進行尋找返回即可。 

無序數組實現 
如果使用無序數組,那麼每一次插入的時候,直接在數組末尾插入即可,時間複雜度為O(1),但是如果要擷取最大值,或者最小值返回的話,則需要進行尋找,這時時間複雜度為O(n)。

要實現刪除最大元素,可以添加一段類似於選擇排序的內迴圈代碼,將最大元素的和邊界元素交換後刪除它,和對棧的pop()方法的實現一樣。同樣也可以加入調整數組的代碼來達到動態調整數組的目的。

有序數組實現 
如果使用有序數組,那麼每一次插入的時候,通過插入排序將元素放到正確的位置,時間複雜度為O(n),但是如果要擷取最大值的話,由於元阿蘇已經有序,直接返回數組末尾的 元素即可,所以時間複雜度為O(1).

在insert方法中添加代碼,將所有較大的元素向右邊移動一格以使數組保持有序(和插入排序一樣)。這樣,最大的元素總會在數組的一邊,刪除最大元素,只需要像棧的pop()一樣就可以了。。

所以採用普通的數組或者鏈表實現,無法使得插入和排序都達到比較好的時間複雜度。所以我們需要二元堆積(binary heap)來實現優先順序隊列 鏈表標記法

我們還可以使用基於鏈表的下壓棧的代碼作為基礎,而後可以選擇修改pop()來找到並返回最大元素,或是修改push()來保證所有元素的逆序並用pop()來刪除並返回鏈表的首元素。 
二元堆積

二元堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的索引值或索引總是小於(或者大於)它的父節點。 有了這一性質,那麼二元堆積上最大值就是根節點了。

二元堆積的表現形式:我們可以使用數組的索引來表示元素在二元堆積中的位置。

從二元堆積中,我們可以得出:

· 元素k的父節點所在的位置為[k/2]

· 元素k的子節點所在的位置為2k和2k+1

跟據以上規則,我們可以使用二維數組的索引來表示二元堆積。通過二元堆積,我們可以實現插入和刪除最大值都達到O(nlogn)的時間複雜度。

對於堆來說,最大元素已經位於根節點,那麼刪除操作就是移除並返回根節點元素,這時候二元堆積就需要重新排列;當插入新的元素的時候,也需要重新排列二元堆積以滿足二元堆積的定義。

從下至上的堆有序變化 
如果一個節點的值大於其父節點的值,那麼該節點就需要上移,一直到滿足該節點大於其兩個子節點,而小於其根節點為止,從而達到使整個堆實現二元堆積的要求。

我們只需要將該元素k和其父元素k/2進行比較,如果比父元素大,則交換,然後迭代,一直到比父元素小為止。

private static void Swim(int k){    //如果元素比其父元素大,則交換    while (k > 1 && pq[k].CompareTo(pq[k / 2]) > 0)    {        Swap(pq, k, k / 2);        k = k / 2;    }}
1 2 3 4 5 6 7 8 9

這樣,往堆中插入新元素的操作變成了,將該元素從下往上重建立堆操作: 

代碼實現如下:

public static void Insert(T s){    //將元素添加到數組末尾    pq[++N] = s;    //然後讓該元素從下至上重建堆    Swim(N);}
1 2 3 4 5 6 7

由上至下的堆有序變化 
當某一節點比其子節點要小的時候,就違反了二元堆積的定義,需要和其子節點進行交換以重建立堆,直到該節點都大於其子節點為止:

private static void Sink(int k){    while (2 * k < N)    {        int j = 2 * k;        //去左右子節點中,稍大的那個元素做比較        if (pq[j].CompareTo(pq[j + 1]) < 0) j++;        //如果父節點比這個較大的元素還大,表示滿足要求,退出        if (pq[k].CompareTo(pq[j]) > 0) break;        //否則,與子節點進行交換        Swap(pq, k, j);        k = j;    }}
1 2 3 4 5 6 7 8 9 10 11 12 13 14

這樣,移除並返回最大元素操作DelMax可以變為:

移除二元堆積根節點元素,並返回

將數組中最後一個元素放到根節點位置

然後對新的根節點元素進行Sink操作,直到滿足二元堆積要求。

移除最大值並返回的操作如下圖所示:

public static T DelMax(){    //根項目從1開始,0不存放值    T max = pq[1];    //將最後一個元素和根節點元素進行交換    Swap(pq, 1, N--);    //對根節點從上至下重建立堆    Sink(1);    //將最後一個元素置為空白    pq[N + 1] = default(T);    return max;}
1 2 3 4 5 6 7 8 9 10 11 12

多叉堆

基於用數組表示的完全三叉樹構造堆並修改相應的代碼並不難,對應數組中1至N的N個元素,位置k的結點大於大於等於3k-1,3k,3k+1的結點,小於位於[(k+1)/3]下取整的結點。 d叉堆

完全d叉樹,根最小。儲存時使用層序。 

操作跟二元堆積基本一致:insert,deleteMin,增大元素,減小元素,刪除非頂元素,merge。

二元堆積與d叉堆的對比: 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.