PriorityQueue實現原理

2021-06-29 12:26:38 字數 3385 閱讀 5939

priorityqueue

priorityqueue是個基於優先順序堆的極大優先順序佇列

。此佇列按照在構造時所指定的順序對元素排序,既可以根據元素的自然順序來指定排序(參閱 comparable),

也可以根據 comparator 來指定

,這取決於使用哪種構造方法。優先順序佇列不允許 null 元素。

依靠自然排序的優先順序佇列還不允許插入不可比較的物件(這樣做可能導致 classcastexception)。

此佇列的頭是按指定排序方式的

最小元素

。如果多個元素都是最小值,則頭是其中乙個元素——選擇方法是任意的。

佇列檢索操作 poll、remove、peek 和 element 訪問處於佇列頭的元素。

優先順序佇列是無界的,但是有乙個

內部容量

,控制著用於儲存佇列元素的陣列的大小。

它總是至少與佇列的大小相同。隨著不斷向優先順序佇列新增元素,其容量會自動增加。無需指定容量增加策略的細節。

注意1:該佇列是用

陣列實現

,但是陣列大小可以

動態增加,容量無限

。注意2

:此實現不是同步的。

不是執行緒安全的

。如果多個執行緒中的任意執行緒從結構上修改了列表, 則這些執行緒不應同時訪問 priorityqueue 例項,這時請使用

執行緒安全的priorityblockingqueue 類

。注意3

:不允許使用 

null 元素

。注意4

:此實現為插入方法(offer、poll、remove() 和 add 方法)提供 o(log(n)) 時間;

為 remove(object) 和 contains(object) 方法提供線性時間;

為檢索方法(peek、element 和 size)提供固定時間。

注意5:方法iterator()中提供的迭代器並

不保證以有序的方式遍歷優先順序佇列中的元素

。至於原因可參考下面關於priorityqueue的內部實現

如果需要按順序遍歷,請考慮使用 arrays.sort(pq.toarray())。

注意6:

可以在建構函式中指定如何排序

。如:priorityqueue()

使用預設的初始容量(11)建立乙個 priorityqueue,並根據其自然順序來排序其元素(使用 

comparable

)。priorityqueue(int initialcapacity)

使用指定的初始容量建立乙個 priorityqueue,並根據其自然順序來排序其元素(使用 

comparable

)。priorityqueue(int initialcapacity, comparator<? super e> comparator)

使用指定的初始容量建立乙個 priorityqueue,並根據指定的比較器

comparator

來排序其元素。

注意7:此類及其迭代器實現了 collection 和 iterator 介面的所有可選 方法。

priorityqueue的內部實現

priorityqueue對元素採用的是堆排序,頭是按指定排序方式的最小元素。堆排序只能保證根是最大(最小),整個堆並不是有序的。

方法iterator()中提供的迭代器可能只是對整個陣列的依次遍歷。也就只能保證陣列的第乙個元素是最小的

。例項1的結果也正好與此相符。

例項1:

public static void main(string args) 

system.out.println();

system.out.println("poll(): " + pq.poll());}}

輸出的結果如下: 

left:boy dog fox easy 

poll(): boy

left:dog easy fox 

poll(): dog

left:easy fox 

poll(): easy

left:fox 

poll(): fox

可以看到,

雖然priorityqueue保持了佇列頂部元素總是最小,但內部的其它元素的順序卻隨著元素的減少始終處於變化之中

。察看源**來一**竟。從netbeans中非常方便的連線到priorityqueue的add函式實現,

最終跟蹤到函式private void siftupcomparable(int k, e x),定義如下:

private void siftupcomparable(int k, e x) 

queue[k] = key;

}相對於add的操作,該函式的入口引數k是指新加入元素的下標,而x 就是新加入的元素。

乍一看,這個函式的實現比較令人費解,尤其是parent的定義。通過進一步分析了解到,

priorityqueue內部成員陣列 queue其實是實現了乙個二叉樹的資料結構,這棵二叉樹的根節點是queue[0],

左子節點是queue[1],右子節點是queue[2],而 queue[3]又是queue[1]的左子節點,依此類推,給定元素queue

,該節點的父節點是queue[(i-1)/2]。因此 parent變數就是對應於下標為k的節點的父節點。

弄清楚了這個用陣列表示的二叉樹,就可以理解上面的**中while 迴圈進行的工作是,

當欲加入的元素小於其父節點時,就將兩個節點的位置交換。這個演算法保證了如果只執行add操作,那麼queue這個二叉樹是有序的:

該二叉樹中的任意乙個節點都小於以該節點為根節點的子數中的任意其它節點。這也就保證了queue[0],即隊頂元素總是所有元素中最小的。

需要注意的是,這種演算法無法保證不同子樹上的兩個節點之間的大小關係。舉例來說,queue[3]一定會小於queue[7],但是未必會小於queue[9],因為queue[9]不在以queue[3]為根節點的子樹上。

弄清楚了add的操作,那麼當佇列中的元素有變化的時候,對應的陣列queue又該如何變化呢?察看函式poll(),

最終追中到函式private e removeat(int i),**如下:

private e removeat(int i) 

}return null;

}這個函式的實現方法是,將隊尾元素取出,插入到位置i,替代被刪除的元素,然後做相應的調整,保證二叉樹的有序,

即任意節點都是以它為根節點的子樹中的最小節點。進一步的**就留給有興趣的讀者自行分析,

要說明的是,對於 queue這樣的二叉樹結構有乙個特性,即如果陣列的長度為length,

那麼所有下標大於length/2的節點都是葉子節點,其它的節點都有子節點。

總結:可以看到這種演算法的實現,充分利用了樹結構在搜尋操作時的優勢,效率又高於維護乙個全部有序的佇列

priority queue 模擬實現

namespace bit 函式物件 less template struct less bool operator const t left,const t right 函式物件 greater template struct greater bool operator const t left,...

C 中priority queue的實現

優先順序佇列相對於普通佇列,提供了插隊功能,每次最先出隊的不是最先入隊的元素,而是優先順序最高的元素。它的實現採用了標準庫提供的heap演算法。該系列演算法一共提供了四個函式。使用方式如下 首先,建立乙個容器,放入元素 vectorcoll insertnums coll,3,7 insertnum...

STL之priority queue實現詳解

優先佇列可以從尾部插入元素,然後從頭部取出優先順序 通過乙個數值表示 最高的物件。這種支援插入,然後每次僅僅取出乙個物件資料結構,完全天然和堆一一對應,所以通過堆實現優先佇列介面卡是天然的選擇。也就是說最大堆其實就是優先佇列。優先佇列沒有迭代器,進出都有一定的規則,只有queue頂端的元素 權重最高...