优先级队列
优先级队列介绍
基于优先级堆的无界优先级队列。优先队列的元素根据它们的自然顺序排序,或者根据使用的构造函数由队列构建时提供的比较器排序。优先队列不允许空元素。依赖于自然排序的优先级队列也不允许插入不可比较的对象(这样做可能会导致ClassCastException)。相对于指定的顺序,此队列的头是最小的元素。如果多个元素为最小值绑定,则head是这些元素之一——绑定是任意断开的。队列检索操作轮询、删除、查看和元素访问队列头部的元素。优先队列是无界的,但是有一个内部容量来控制用于在队列中存储元素的数组的大小。它总是至少与队列大小一样大。当元素被添加到优先队列时,它的容量会自动增长。增长策略的细节没有指定。该类及其迭代器实现集合和迭代器接口的所有可选方法。方法Iterator()中提供的迭代器不能保证以任何特定的顺序遍历优先队列的元素。如果需要有序遍历,可以考虑使用array.sort(pq.toArray())。注意,这个实现不是同步的。如果任何线程修改队列,多线程不应该并发地访问PriorityQueue实例。相反,应该使用线程安全的java.util.concurrent.PriorityBlockingQueue
类。实现说明:此实现为入队和出队方法(offer,poll,remove()和add)提供O(log(n))时间;remove(Object)和contains(Object)方法的线性时间;以及检索方法(peek, element和size)的常数时间。该类是Java集合框架的成员。
数据结构
使用线性表来存储平衡二叉最小堆
transient Object[] queue;
基于线性表的算法设计:
最后一个非叶子节点索引: lastNonLeafIndex= size <<< 1 - 1
判断有无子节点: hasChild(n) = n < ( size << 2 )
求子节点索引: leftChildIndex=n*2-1, rightChildIndex=leftChildIndex+1,注意判断索引越界
算法图解
初始数组值(未构造最小堆): [9,8,7,6,4,3,2,1]
初始二叉堆的图形如下
平衡二叉最小堆的构造
首先找到最大的非叶子节点ID,与它的两个子节点比较,将其中的较小值和它交换,如果它最小的子节点还有子节点,跳到较小节点的位置,重复该过程,否则退出.
第二步: 移动到倒数第二个非叶子节点,重复第一步的过程.
第三步: 继续到下一个非叶子节点,直到根节点,重复前两步的过程
节点1和节点3交换值,因为节点数据发生交换,且节点3还有子节点,所以需要继续处理节点3;
如果未发生节点值交换,可以不用继续处理子节点;
交换节点3和节点7的值,节点7没有子节点了,退出本轮循环.
处理下一个非叶子节点0,交换节点0和节点1的值.
同理,继续交换节点1和节点3的值.
同理,继续交换节点3和节点8的值,到此,平衡二叉最小堆构建完成
节点新增算法
节点新增的算法步骤:
- 在堆的最后新增一个叶子节点,这一步可能需要扩容(见后文如何JDK的扩容策略);
- 和它的父节点比较,如小于等于父节点,则交换值,否则插入完成;
- 跳到父节点,因为发生了值交换,所以需要和当前节点的父节点再进行交换比较,重复该过程,直到根节点;
-
若新增值为3
-
交换4,5节点的值
-
因为节点4大于父节点,故操作完成
根节点移除
算法步骤:
- 将最后一个节点和根节点交换.
2.以根节点未起点,向下交换较小的节点.直到无子节点或比子节点都小.
-
移出根节点值,将节点9的值赋值给根节点, 节点9的值赋值null
-
节点1值小于根节点,交换值, 因为有子节点,且存在更小的值,需要继续该过程
-
交换节点1和节点4的值,操作完成
节点值索引查询
算法步骤:
1.从0到size-1,一次比较数组值和目标值,返回相等时的索引
节点删除
算法步骤:
- 交换删除节点和尾节点的值;
- 由于发生了值交换,需要应用下沉或上浮来确保树的性质;
-
情况1: 替换值后发生下沉操作
-
情况2: 替换值后发生上浮操作
-
情况三: 即没有上浮也没有下沉
迭代器
迭代器next()会导致遍历指针+1, remove()会移除最后一次迭代的元素;
在迭代的过程中如果调用 remove()会由于上浮下沉出现
- 未访问的尾元素被移动到已经访问的位置(节点删除情况3)或
- 其他未访问的位置(节点删除情况2)或
- 当前删除的元素位置(节点删除情况3)三种情况.
所以为了能完整的遍历出所有的元素,对于情况1和情况3,需要让next的指针减1来访问尾节点或被下沉中交换上来的元素;对于情况2,需要保存尾节点元素,最后再访问这些漏掉的元素;
扩容
扩容前容量小于64:容量翻倍
否则: 增长50%
扩容后检查最大容量限制: Integer.MAX_VALUE - 8 或者 Integer.MAX_VALUE
/**
* Increases the capacity of the array.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
引用
java.util.PriorityQueue
Sorce Code