最近在專案開發中開發了全雙工非同步長連線的通訊元件,內部用到了延遲佇列。而延遲佇列的內部實現的儲存是用到了優先佇列,當時看c++的資料結構時,了解過優先佇列,用的儲存是二叉樹的邏輯,應該叫完全二叉樹,也可以叫做最大堆。
下面看一下二叉樹的演算法,主要看插入和刪除。
二叉樹顧名思義就像一棵樹,每個節點下最多可以掛兩個節點,如圖
在優先佇列中儲存的方式就是 queue = ;
若當前節點下標為i 那麼它的子節點 左側 = 2*i+1 右側 = 2*i +2。優先佇列顧名思義,就是優先權最大的排在佇列的頭部,而優先權的判斷是根據物件的compare方法比較獲取的,保證根節點的優先順序一定比子節點的優先順序大。所以放入到優先佇列的元素要麼實現了comparable介面,要麼在創造這個優先佇列時,指定乙個比較器。
下面我們來看一下優先佇列的建構函式
private static final int default_initial_capacity = 11;
private final comparator super e> comparator;
public priorityqueue()
public priorityqueue(int initialcapacity,
comparator super e> comparator)
在空構造器初始化時,陣列佇列的大小為11,比較器為null。這樣的話,放入優先佇列的物件要實現comparable介面,也可以判斷佇列中不能存放null元素。
現在我們來看一下往佇列中新增乙個物件的過程。
public boolean add(e e)
public boolean offer(e e)
可以看到,優先佇列中不可能存在null元素,新增元素首先把佇列修改次數++,判斷是否超過內部陣列的長度,超過後增加陣列的長度 grow(i+1)
private void grow(int mincapacity)
如果陣列長度小於64 是按照一倍長度增長的,長度超過64,每次增長原來的百分之50,如果長度超過了int的最大值,那就設定為
integer.max_value,如果新的長度小於需要增長的長度,就是設定成這個長度,最後複製原來的物件到新的陣列。
這樣陣列長度擴充套件了,設定size+1。如果是空的優先佇列,就把新元素新增到佇列頭部。如果不是空,那就根據compareto來判斷新加入的元素的優先級別,那麼我們來看一下siftup方法
private void siftup(int k, e x)
private void siftupcomparable(int k, e x)
queue[k] = key;
}private void siftupusingcomparator(int k, e x)
queue[k] = x;
}
如果在建立佇列的時候,指定了比較工具,那麼就用比較器,比較器內部實現,可以根據自己的定義的比較原則,也可以呼叫加入佇列的元素的compareto方法(如果實現的話),比較方法變化自行定義。
我們主要看一下沒有比較器的排序方法siftupcomparable,如下圖
假設數字越小,優先級別越高,k的位置是新插入元素將要放置的位置,加入插入的是 25,那麼此元素的優先級別小於它的父節點的優先順序,直接把25放在k的位置。假如放置的是8,優先級別最高,所以 (k-1)>>>1 獲取父節點,與父節點比較,發現優先順序大於父節點,把父節點的值放入k, 把k的位置設定成父節點的下標值,遞迴查詢父節點,定位出k的位置,把新元素放入此位置。此種二叉樹演算法大大降低了演算法複雜度。
接下來我們看一下取走佇列頭部元素
public e poll()
private void siftdown(int k, e x)
private void siftdowncomparable(int k, e x)
queue[k] = key;
}private void siftdownusingcomparator(int k, e x)
queue[k] = x;
}
如果佇列之有乙個元素,就彈出這個元素,設定--size的位置為null。如果不是乙個元素,彈出第乙個元素,獲取陣列最後的乙個元素,開始呼叫siftdown(0, x);我們先來看一下**
這樣到poll掉佇列頭部元素後,佇列長度減一,所以最後的那個元素32的位置需要設定成null,這樣一來,需要填補佇列的頭,由於子節點一定小於等於父節點的優先順序,獲取父節點的子節點
int child = (k << 1) + 1; // 獲取左子節點的下標
object c = queue[child]; // 獲取左子節點
int right = child + 1; // 獲取右子節點的下標
但右子節點不一定存在所以下面做了乙個右節點不會為null的判斷,不為null的情況下,獲取兩個節點中優先順序高的乙個
if (right < size &&((comparable super e>) c).compareto((e) queue[right]) > 0)
//而下面這段**的意思是可能出現如下圖的情況
當21移到佇列頭部時,佇列尾部的 32元素的優先順序 大於68和67的優先順序,可以直接把32元素放入到21的位置,直接結束迴圈,減少了下面空出元素的位置的重新排序演算法。
用while(k
優先佇列 也就這兩個比較重要的方法,對於remove(object o)方法,無非是刪除中間節點後的一種先向下排序,再向上排序的過程,可以自行檢視。
歡迎交流。
stl之佇列,雙端佇列,優先佇列
前提 知道什麼是佇列,雙端佇列和優先佇列 1,佇列 標頭檔案 include queue 宣告 queue class q 基本操作 push x 將x壓入佇列的末端 pop 彈出佇列的第乙個元素 隊頂元素 注意此函式並不返回任何值 front 返回第乙個元素 隊頂元素 back 返回最後被壓入的元...
演算法之優先佇列
1 概念 優先順序佇列,顧名思義,就是一種根據一定優先順序儲存和取出資料的佇列。它可以說是佇列和排序的完美結合體,不僅可以儲存資料,還可以將這些資料按照我們設定的規則進行排序。優先順序佇列是堆的一種常見應用。有最大優先順序佇列 最大堆 和最小優先順序佇列 最小堆 優先順序佇列是一種維護有一組元素構成...
C STL之優先佇列
cpp view plain copy struct cmp1 struct cmp2 struct node1 struct node2 priority queue q1 採用預設優先順序構造佇列 priority queue,vector int cmp1 q2 最小值優先 priority ...