优先队列的本质就是一个堆。 本质是一个数组维护的堆结构。在PriorityQueue中定义了 一个 object数组, 可以指定初始容量, 默认的初始容量
DEFAULT_INITIAL_CAPACITY = 11
需要注意的是: public PriorityQueue(int initialCapacity) 中只是限制了他的初试容量。 但是,只要加入新的元素,该容器就可能会扩容。 所以 java中, 优先队列不能直接限制容量。
比如: 我们要在n 个 数中找到 前k大的数。 若不加以其他限制,最终容器中会有n个元素,而不是K个。
手动限制优先队列大小(无法通过某参数直接限制大小)
找前 K 大 : 用小顶堆
在这里,我们可以利用堆的思想:建立一个小顶堆,然后遍历「出现次数数组」:
如果堆的元素个数小于 k,就可以直接插入堆中。
如果堆的元素个数等于 k,则检查堆顶与当前出现次数的大小。如果堆顶更大,说明至少有 k 个数字的出现次数比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中。通过该方式,可以保持 堆的 大小 不超过 k
for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
int num = entry.getKey(), count = entry.getValue();
// 若 堆 中 元素 已经有 K 个。
if (queue.size() == k) {
// 若 新 元素 大于 堆顶, 则 替换 堆顶。
if (queue.peek()[1] < count) {
queue.poll();
queue.offer(new int[]{num, count});
}
} else {
queue.offer(new int[]{num, count});
}
}
一个很容易犯错的误区
堆 可以 实现 排序。
但是,在任意时刻, 堆的数组 ,都是无序的。不能通过下标 来访问 堆。
小堆顶与大顶堆
默认为小顶堆, 通过设置比较器来实现大顶堆
PriorityQueue<Integer> q=new PriorityQueue<Integer>(10, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
容量
// Double size if small; else grow by 50%小于64 : 每次容量翻倍
否则 : 容量加 50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));