優先隊列PriorityQueue

leetcode刷題是遇到一道按照二進制1的個數排列數組元素的題;

* 給你一個整數數組 arr 。請你將數組中的元素按照其二進制表示中數字 1 的數目升序排序。
* 如果存在多個數字二進制中 1 的數目相同,則必須將它們按照數值大小升序排列。
* 請你返回排序後的數組。

一般我們都是通過函數  num &= num - 1來計算二進制中1的個數,這個肯定是可以的

public int countBit1(int num) {
    int count = 0;
    while (num != 0) {
        num &= num - 1;  //去掉一個低位1
        count++;
    }
    return count;
}

但是我做完,再看其他人的解法時,發現有個使用優先隊列來存儲和排序數組的方法,覺得也十分巧妙

PriorityQueue<Integer> pQ = new PriorityQueue<>(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        if (Integer.bitCount(o1) == Integer.bitCount(o2)) {
            return o2 - o1;
        }
        return Integer.bitCount(o1) - Integer.bitCount(o2);
    }
});

該方法主要使用Integer自帶的bitCount方法,來計算1的個數,通過重寫Comparator方法,來自定義PriorityQueue的排序原則

對此我對優先隊列的源碼進行的一些研究

1,優先隊列的數據結構

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable {
    transient Object[] queue;    //隊列容器, 默認是11
    private int size = 0;  //隊列長度
    private final Comparator<? super E> comparator;  //隊列比較器, 爲null使用自然排序
    //....
}

和常規的隊列相比多了一個Comparator方法,通過重寫這個方法來自定義排序規則

 

2,優先隊列是線程安全的嗎?

我們來看入隊的offer方法

 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;
    }

可以看出PriorityQueue並不是線程安全隊列,因爲offer都沒有對隊列進行鎖定,如果要擁有線程安全的優先級隊列,需要額外進行加鎖操作 

 

3,如何進行排序的?

兩種方式,指定比較器和使用默認比較器

  private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);   //指定比較器
        else
            siftUpComparable(k, x);   //沒有指定比較器,使用默認的自然比較器
    }

這裏主要關注自定義比較器,使用選擇排序法將入列的元素放左邊或者右邊.
從源碼上看PriorityQueue的入列操作並沒對所有加入的元素進行優先級排序。僅僅保證數組第一個元素是最小的即可。

  private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }

 所以當我們每次通過poll方法放回第一個元素時,我們就可以獲得最小的元素(假設升序排列),依次類推,當所有元素出隊列   時,就可以做到全局有序

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