1.优先级队列
(1)概念
普通队列有着先进先出的性质
比如:入队顺序:3,1,2
出队顺序:3,1,2
但优先级队列不同
比如:入队顺序:3,1,2
出队顺序:1,2,3(此种为队首为最小元素的优先级队列)
或3,2,1(此种为队首为最大元素的优先级队列)
因为优先级队列有着这样的特性,所以它存放的元素必须是可以比较大小的。
(2)优先级队列的特性:
- PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
- ClassCastException异常
- 不能插入null对象,否则会抛出NullPointerException
- 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
- 插入和删除元素的时间复杂度为
- PriorityQueue底层使用了堆数据结构
(3)优先级队列的构造方法
这里只列举常见的几种,详细的请查询jdk-api。
/*无参构造方法:底层默认容量为11*/
ProirityQueue<Integer> queue = new PriorityQueue<>();
/*创建一个空的优先级队列,底层的容量为initialCapacity*/
PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
// 用ArrayList对象来构造一个优先级队列的对象
ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
(4)优先级队列的常见操作方法
2.底层实现----堆
将优先队列里存储的N个元素看成集合K={K0,K1,K2,…,Kn-1}按照完全二叉树的顺序存储方式存储在一维数组中:
如果:
- Ki <= K2i+1 且 Ki<= K2i+2,称这样的集合为小堆
- Ki >= K2i+1 且 Ki>= K2i+2,称这样的集合为大堆
(1)堆的性质: 堆中的某个结点的值总是不大于(大堆)或不小于(小堆)其父节点的值。
(2)堆的存储方式:
堆是一棵完全二叉树,可以按照其层序遍历的顺序存储在一维数组。
对于存储在数组中的完全二叉树,它的特点可以利用数组下标表示:(i为数组下标编号,N为节点总数)
- 若i=0,i所对应的元素为根结点,它没有双亲结点;若i!=0,则它的双亲结点下标为(i-1)/2;
- 若2i+1<N,则此节点左孩子下标为2i+1,否则它就没有左孩子;
- 若2i+2<N,则此节点左孩子下标为2i+2,否则它就没有右孩子;
3.堆的创建
(1)向下调整
对于这样一个完全二叉树,如何将它变成堆(此处实现的是小堆)。
向下调整
向下调整适用于根结点的左右子树已经满足堆的性质的情况
例如这个堆,4的左子树和右子树均已满足小堆的性质,直接使用一次向下调整就可以将整个堆变成小堆。
向下调整实现:
记录根结点为parent,孩子结点为child
void shiftDown(int[] array,int parent){
int child = parent*2+1;
//child总是标记最大的孩子,此处先让他标记左孩子。因为一个结点不一定有右孩子。
while(child < size ){
//有孩子比左孩子大,child标记右孩子
if(child+1 < size && array[child+1] > array[child]){
child = child + 1;
}
if(array[parent] <= array[child]){
break;//满足小堆
}else{
swap(int[] array, int parent, int child);//交换值
parent = child;
child = parent*2 + 1;//让child再次标记parent的左孩子
}
}
}
构造堆:
从底层构造堆,反复调用向下调整:
int[] array={5,3,0,6,2,8,4,7,1,6,9}
//jdk-api中没有此种构造方法,这里只是样例
public MyPriorityQueue(int array[]){
int root = (array.length-2)>>1;//找到最后一个非叶子节点
for(; root >= 0; root--){
shiftDown(array,root);
}
}
从小堆中删除堆顶元素:
将堆中最后一个元素与堆顶元素交换,array.length-1,然后再做一次向下调整。
向上调整
向小堆中添加元素:
向堆中添加元素,要从末尾添加。此时已打破了小堆的性质,这时,也不能使用向下调整,此时,根结点的左右子树已经不满足小堆的性质。
void shiftUp(int[] array,int child){
int parent = (child-1)>>1;
while(parent >= 0){
if(array[parent] <= array[child]){
break;//满足小堆性质
}else{
swap(array,child,parent);
child = parent;
parent = (child-1)>>1;
}
}
}