優(yōu)先級(jí)隊(duì)列是一種抽象數(shù)據(jù)類(lèi)型。優(yōu)先隊(duì)列中的每個(gè)元素都有各自的優(yōu)先級(jí),優(yōu)先級(jí)最高的元素最先得到服務(wù);優(yōu)先級(jí)相同的元素按照其在優(yōu)先隊(duì)列中的順序得到服務(wù)。優(yōu)先隊(duì)列往往用堆(數(shù)據(jù)結(jié)構(gòu))來(lái)實(shí)現(xiàn)。
初級(jí)實(shí)現(xiàn)
有許多簡(jiǎn)單低效的實(shí)現(xiàn)。如用一個(gè)有序的數(shù)組;或使用無(wú)序數(shù)組,在每次取出時(shí)搜索全集合,這種方法插入的效率為O(1),但取出時(shí)效率為?O(n)。
典型實(shí)現(xiàn)
出于性能考慮,優(yōu)先隊(duì)列用堆 (數(shù)據(jù)結(jié)構(gòu))來(lái)實(shí)現(xiàn),具有O(log n)時(shí)間復(fù)雜度的插入元素性能,O(n)的初始化構(gòu)造的時(shí)間復(fù)雜度。如果使用自平衡二叉查找樹(shù),插入與刪除的時(shí)間復(fù)雜度為O(log n),構(gòu)造二叉樹(shù)的時(shí)間復(fù)雜度為O(n log n)。
源碼分析
繼承關(guān)系
public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable
無(wú)參構(gòu)造函數(shù)
private static final int DEFAULT_INITIAL_CAPACITY = 11;
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
transient Object[] queue;
private final Comparator<? super E> comparator;
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
無(wú)參構(gòu)造調(diào)用了int
和Comparator
參數(shù)的構(gòu)造方法,可以看到PriorityQueue
底層是一個(gè)Object[]
,默認(rèn)初始容量是11,比較器默認(rèn)為空,自然排序。
Collection參數(shù)構(gòu)造
public PriorityQueue(Collection<? extends E> c) {
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
this.comparator = (Comparator<? super E>) ss.comparator();
initElementsFromCollection(ss);
}
else if (c instanceof PriorityQueue<?>) {
PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
this.comparator = (Comparator<? super E>) pq.comparator();
initFromPriorityQueue(pq);
}
else {
this.comparator = null;
initFromCollection(c);
}
}
private void initElementsFromCollection(Collection<? extends E> c) {
Object[] a = c.toArray();
if (a.getClass() != Object[].class)
a = Arrays.copyOf(a, a.length, Object[].class);
int len = a.length;
if (len == 1 || this.comparator != null)
for (Object e : a)
if (e == null)
throw new NullPointerException();
this.queue = a;
this.size = a.length;
}
private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
if (c.getClass() == PriorityQueue.class) {
this.queue = c.toArray();
this.size = c.size();
} else {
initFromCollection(c);
}
}
private void initFromCollection(Collection<? extends E> c) {
initElementsFromCollection(c);
heapify();
}
Collection
參數(shù)構(gòu)造分三種類(lèi)型處理
- 1.SortedSet
SortedSet
是一個(gè)接口,實(shí)現(xiàn)類(lèi)是TreeSet
,在這里可以認(rèn)為是有序的Set
,在initElementsFromCollection
方法中將原SortedSet
中的元素按照原來(lái)的順序賦值給了自身的queue
- 2.PriorityQueue
如果是PriorityQueue
,將按原來(lái)的元素順序賦值給自身的queue
- 3 其他
按照自然順序賦值,調(diào)用heapify()
將數(shù)據(jù)調(diào)整為二叉堆
二叉堆是一種特殊的堆,是一棵完全二叉樹(shù)或者是近似完全二叉樹(shù),同時(shí)二叉堆還滿(mǎn)足堆的特性:父節(jié)點(diǎn)的鍵值總是保持固定的序關(guān)系于任何一個(gè)子節(jié)點(diǎn)的鍵值,且每個(gè)節(jié)點(diǎn)的左子樹(shù)和右子樹(shù)都是一個(gè)二叉堆。
當(dāng)父節(jié)點(diǎn)的鍵值總是大于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值時(shí)為最大堆。 當(dāng)父節(jié)點(diǎn)的鍵值總是小于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值時(shí)為最小堆。
Comparator參數(shù)構(gòu)造
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
默認(rèn)數(shù)組長(zhǎng)度,將comparator
比較器賦值
add(E e)和offer(E e)
public boolean add(E e) {
return offer(e);
}
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}
private void grow(int minCapacity) {
int oldCapacity = queue.length;
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
add()
調(diào)用offer()
,兩個(gè)方法走相同邏輯
- 1.判斷插入元素是否為空,不允許插入
null
- 2.修改次數(shù)++
- 3.元素個(gè)數(shù)大于等于
queue
的容量就擴(kuò)容,擴(kuò)容規(guī)則:
原容量長(zhǎng)度小于64,則增加原容量長(zhǎng)度+2的長(zhǎng)度,即擴(kuò)容后的長(zhǎng)度等于原長(zhǎng)度的2倍+2;原容量長(zhǎng)度大于等于64,則新增原容量長(zhǎng)度的>>1,即新增原容量長(zhǎng)度的一半
- 4.size+1,這里是size并非數(shù)組的長(zhǎng)度,而是元素個(gè)數(shù)
- 5.如果是第1個(gè)添加的元素,就將第0個(gè)索引賦值,如果不是第一個(gè)添加的元素,將元素加入二叉堆“上移”。
上移siftUp(int k, E x) 方法在這里著重分析下,如果比較器comparator != null,則走siftUpUsingComparator(k, x)方法,否則走siftUpComparable(k, x),siftUpComparable(k, x)方法則需要元素實(shí)現(xiàn)Comparable接口進(jìn)行比較。siftUpUsingComparator(k, x)和siftUpComparable(k, x)上移邏輯是一樣的。
siftUpComparable(k, x)方法分析
- k>0表示判斷k不是根的情況下,也就是元素x有父節(jié)點(diǎn)
- 如果不是根節(jié)點(diǎn),通過(guò)(k - 1) >>> 1拿到父節(jié)點(diǎn),二叉堆獲取父節(jié)點(diǎn)位置公式(n-1)/2
- 如果新增的元素k比其父節(jié)點(diǎn)e大,則不需要"上移",跳出循環(huán)結(jié)束,否則與父節(jié)點(diǎn)交換位置,并將k指向父節(jié)點(diǎn)位置,進(jìn)入下一層循環(huán)
- 找到新增元素x的合適位置k之后進(jìn)行賦值
poll()
public E poll() {
if (size == 0)
return null;
int s = --size;
modCount++;
E result = (E) queue[0];
E x = (E) queue[s];
queue[s] = null;
if (s != 0)
siftDown(0, x);
return result;
}
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
poll()出隊(duì)對(duì)于被刪除元素來(lái)說(shuō),是下移。poll()主要邏輯
- 1.隊(duì)列為空,返回
null
- 2.元素個(gè)數(shù)-1
- 3.修改次數(shù)++
- 4.取出隊(duì)首元素
result
,隊(duì)尾元素x
- 5.當(dāng)隊(duì)尾索引不等于0,也就是隊(duì)列中多于1個(gè)元素,下移,siftDown(int k, E x)邏輯:
下移siftDown(int k, E x) 方法在這里著重分析下,如果比較器comparator != null,則走siftDownUsingComparator(k, x)方法,否則走siftDownComparable(k, x),siftDownComparable(k, x)方法則需要元素實(shí)現(xiàn)Comparable接口進(jìn)行比較。siftDownUsingComparator(k, x)和siftDownComparable(k, x)下移邏輯是一樣的。
siftDownUsingComparator(int k, E x)分析
參數(shù)說(shuō)明:k表示移除元素下標(biāo)0,x表示隊(duì)尾元素
- int half = size >>> 1,無(wú)符號(hào)右移1位,等同于int half = size / 2 , 得到葉子節(jié)點(diǎn)索引half。
- 循環(huán)k < half,表示,下移最多下移到葉子節(jié)點(diǎn)。
- int child = (k << 1) + 1,獲取左子節(jié)點(diǎn)索引,等同于int child = k * 2 + 1,右子節(jié)點(diǎn)索引等于左子節(jié)點(diǎn)索引+1
- Object c = queue[child],c暫時(shí)表示左子節(jié)點(diǎn)的元素
- 當(dāng)存在右子節(jié)點(diǎn),并且通過(guò)比較器比較,c賦值為右子節(jié)點(diǎn)元素
- 如果隊(duì)尾元素x比k索引的元素左右子節(jié)點(diǎn)都要小,則不需"下移",結(jié)束循環(huán)
- 將queue索引k賦值為子節(jié)點(diǎn)的元素
- k = child,將k賦值為子節(jié)點(diǎn)索引,進(jìn)入下一層循環(huán)
- 結(jié)束循環(huán)后將x 插入合適的位置
總結(jié)
- 1.PriorityQueue基于二叉堆實(shí)現(xiàn)
- 2.非線(xiàn)程安全
- 3.元素不能為null
- 4.PriorityQueue不是完全有序的,堆頂存儲(chǔ)著最高優(yōu)先級(jí)的元素
- 5.插入和移除元素時(shí),按照優(yōu)先級(jí)調(diào)整根節(jié)點(diǎn)元素