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方法放回第一個元素時,我們就可以獲得最小的元素(假設升序排列),依次類推,當所有元素出隊列 時,就可以做到全局有序