二叉堆是实现优先队列的基础,上一篇二叉堆博文: 二叉堆
队列的特点是先进先出(FIFO)。
优先队列不再遵循先入先出的原则,而是分为两种情况。
- 最大优先队列,无论入队顺序如何,都是当前最大的元素优先出队
- 最小优先队列,无论入队顺序如何,都是当前最小的元素优先出队
最大优先队列
实现方式
可以用最大堆来实现最大优先队列,这样的话,每一次入队操作就是堆的插入操作,每一次出队操作就是删除堆顶节点。
入队
入队就是在数组末尾加上入队元素,在调用最大堆上浮函数,把这个数字上浮到合适的位置
出队
出队就是把数组第一个元素取出,在把数组末尾的元素填到第一个位置,在调用最大堆下沉函数
二叉堆节点“上浮”和“下沉”的时间复杂度都是O(logn),所以优先队列入队和出队的时间复杂度也是O(logn)!
JAVA实现
package dataStructure.myPriorityQueue;
import java.util.Arrays;
//最大优先队列
public class priorityQueue {
private int size;
private int[] array;
public priorityQueue() {
//优先队列初始长度为32
array = new int[32];
}
/**
* 上浮(入队)
*/
private void upAdjust(){
//都是从数组最后一个位置(size-1;)入队
int childIndex = size-1;
int parentIndex = (childIndex-1)/2;
// temp 保存插入的叶子节点值,用于最后的赋值
int temp = array[childIndex];
//最大堆(最大优先队列)
while (childIndex>0&&temp>array[parentIndex]){
array[childIndex] = array[parentIndex];
childIndex = parentIndex;
parentIndex = parentIndex/2;
}
array[childIndex] = temp;
}
/**
* 下沉(出队)
*/
private void downAdjust(){
// temp 保存父节点的值,用于最后的赋值
//删除的是根节点(最大值出队)
int parentIndex = 0;
int temp = array[parentIndex];
//左孩子下标
int childIndex = 1;
while (childIndex<size){
// 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
//需要从左右孩子找最大的(最大堆)
if(childIndex+1<size&&array[childIndex+1]>array[childIndex]){
childIndex++;
}
// 如果父节点大于任何一个孩子的值,直接跳出(已经是最大堆了)
if(temp>=array[childIndex]){
break;
}
array[parentIndex] = array[childIndex];
//再向下找
parentIndex = childIndex;
childIndex = 2*childIndex+1;
}
array[parentIndex] = temp;
}
/**
* 队列扩容
*/
private void resize(){
int newSize = this.size*2;
this.array = Arrays.copyOf(this.array,newSize);
}
/**
* 入队
* @param key 入队的值
*/
public void enQueue(int key){
if(size>=array.length){
resize();
}
//数组最后一个位置入队
array[size++] = key;
//再进行堆上浮
upAdjust();
}
public int deQueue() throws Exception{
if (size<0){
throw new Exception("空的最大优先队列,出队失败");
}
//获取堆顶元素
int head = array[0];
//把最后一个元素移动到堆顶
array[0] = array[--size];
//堆下沉
downAdjust();
return head;
}
public static void main(String[] args) throws Exception {
priorityQueue priorityQueue = new priorityQueue();
priorityQueue.enQueue(3);
priorityQueue.enQueue(5);
priorityQueue.enQueue(10);
priorityQueue.enQueue(10);
priorityQueue.enQueue(2);
priorityQueue.enQueue(7);
System.out.println("最大优先队列出队:"+priorityQueue.deQueue());
}
}
总结
优先队列分为最大优先队列和最小优先队列。
- 在最大优先队列中,无论入队顺序如何,当前最大的元素都会优先出队,这是基于最大堆实现的。
- 在最小优先队列中,无论入队顺序如何,当前最小的元素都会优先出队,这是基于最小堆实现的
二叉堆是一种特殊的完全二叉树,分为最大堆和最小堆。
- 在最大堆中,任何一个父节点的值,都大于或等于它左、右孩子节点的值。
- 在最小堆中,任何一个父节点的值,都小于或等于它左、右孩子节点的值。