PriorityQueue優先隊列實現原理

轉載: http://blog.csdn.net/lisuyibmd/article/details/53205403
一、什麼是優先隊列

優先隊列不是按照普通對象先進先出原FIFO則進行數據操作,其中的元素有優先級屬性,優先級高的元素先出隊。本文提到的PriorityQueue隊列,是基於最小堆原理實現。

需要注意:PriorityQueue繼承了AbstractQueue沒有實現BlockingQueue接口,所以沒有take阻塞方法。

二、什麼是最小堆

最小堆是一個完全二叉樹,所謂的完全二叉樹是一種沒有空節點的二叉樹。

最小堆的完全二叉樹有一個特性是根節點必定是最小節點,子女節點一定大於其父節點。還有一個特性是葉子節點數量=全部非葉子節點數量+1

在 PriorityQueue隊列中,基於數組保存了完全二叉樹。所以在已知任意一個節點在數組中的位置,就可以通過一個公式推算出其左子樹和右子樹的下標。已知節點下標是i,那麼他的左子樹是2*i+1,右子樹是2*i+2。

三、PriorityQueue隊列的存取原理

 1、首先看下源碼

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片

/** 
 * Inserts item x at position k, maintaining heap invariant by 
 * promoting x up the tree until it is greater than or equal to 
 * its parent, or is the root. 
 * 
 * To simplify and speed up coercions and comparisons. the 
 * Comparable and Comparator versions are separated into different 
 * methods that are otherwise identical. (Similarly for siftDown.) 
 * 
 * @param k the position to fill 
 * @param x the item to insert 
 */  
private void siftUp(int k, E x) {  
    if (comparator != null)  
        siftUpUsingComparator(k, x);  
    else  
        siftUpComparable(k, x);  
}  

在調用offer(E e)方法入隊時,首先進行非null判斷,然後是grow方法擴容,緊接着就是siftUp方法調整節點位置,那麼是如何調整的呢?爲什麼要調整?
2、爲什麼要調整節點順序?

因爲這是一個最小堆,最小堆必須要嚴格按照子節點大於父親節點的順序做數組中存放。

3、如何調整?

siftup方法有個if-else判斷,如果有比較器,則使用siftUpUsingComparator(k, x);如果沒有則調用siftUpComparable(k, x);這個方法會默認給一個比較器。

比較什麼呢??我們說最小堆實現了這個隊列,隊列一定有入隊操作,入隊是元素首先進入隊列尾,然後和自己的父節點比較,像冒泡一樣將該節點冒到合適的位置,即比自己的父親節點大,比自己的兒子節點小。

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
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;  
   }  
4、如何出隊
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
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 siftDownComparable(int k, E x) {  
        Comparable<? super E> key = (Comparable<? super E>)x;  
        int half = size >>> 1;        // loop while a non-leaf  
        while (k < half) {  
            int child = (k << 1) + 1; // assume left child is least  
            Object c = queue[child];  
            int right = child + 1;  
            if (right < size &&  
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)  
                c = queue[child = right];  
            if (key.compareTo((E) c) <= 0)  
                break;  
            queue[k] = c;  
            k = child;  
        }  
        queue[k] = key;  
    }  

出隊和入隊原理正好相反,每次出隊也要進行siftDown操作,對元素順序重新整理。

四、總結

PriorityQueue隊列不適合進場出隊入隊的頻繁操作,但是他的優先級特性非常適合一些對順序有要求的數據處理場合。

版權聲明:沉澱、積累,我追求工匠精神

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章