目录
1、优先级队列概念
队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。
JDK1.8中的PriorityQueue底层使用了堆这种数据结构。
2、模拟实现优先级队列
下面以小堆为例,即谁比较小,谁先出队列。(假设不考虑扩容)
public class MyPriorityQueue {
//假设不考虑扩容
int[] arr=new int[10];
int size; //元素个数
public MyPriorityQueue(){
//空的优先级队列
this.size=0;
}
//查看优先级队列最小值
public int peek(int[] arr){
if(this.size==0){
throw new RuntimeException("空的");
}
//第一个元素为最小值
return arr[0];
}
//传入数组以及待调整的结点的下标
public static void adjustDown递归(int[] arr,int parentIdx,int size){
int childLeftIdx=2*parentIdx+1;
//1.说明没有左孩子,是一个叶子结点
if(childLeftIdx>=size){
return;
}
int childRightIdx=2*parentIdx+2; //定义右孩子
int minIdx=childLeftIdx; //先假定左右孩子中最小的是左孩子
//如果右子树存在且右子树比左子树小,重置最小节点
if(childRightIdx<size&&arr[childLeftIdx]>arr[childRightIdx]){
minIdx=childRightIdx;
}
//比较最小值结点与待调整结点的大小
if(arr[parentIdx]<arr[minIdx]){
return;
}
if(arr[parentIdx]>arr[minIdx]){
int tmp=arr[parentIdx];
arr[parentIdx]=arr[minIdx];
arr[minIdx]=tmp;
}
//再递归判断被交换的结点是否满足堆的性质
adjustDown递归(arr,minIdx,size);
}
//删除队首元素
public int poll(){
if(this.size==0){
throw new RuntimeException("空的,删除错误");
}
//将待删除元素与最后一个叶子结点交换,然后向下调整
int e=arr[0];
arr[0]=arr[size-1];
arr[size-1]=0;
size--;
adjustDown递归(arr,0,size);
return e;
}
//插入元素
//插入的最后的位置上,然后向上调整
public void offer(int e){
arr[size]=e;
size++;
int index=size-1;
//index == 0 说明是根,不需要调整了
while (index!=0) {
// 找到child的双亲
int parentIdx = (index - 1) / 2;
// 如果双亲比孩子小,parent满足小堆的性质,调整结束
if (arr[parentIdx] <= arr[index]) {
break;
} else{
// 将双亲与孩子节点进行交换
int tmp=arr[parentIdx];
arr[parentIdx]=arr[index];
arr[index]=tmp;
// 大的元素向下移动
index = parentIdx;
}
}
}
测试:
public static void main(String[] args) {
MyPriorityQueue mpq=new MyPriorityQueue();
mpq.offer(6);
mpq.offer(3);
mpq.offer(7);
mpq.offer(5);
mpq.offer(2);
mpq.offer(4);
mpq.offer(1);
mpq.offer(8);
System.out.println("向队列中添加元素:");
for(int i:mpq.arr){
System.out.print(i+" ");
}
System.out.println();
System.out.println("----------------------");
System.out.println("查看最小值:");
System.out.println(mpq.peek(mpq.arr));
System.out.println("----------------------");
System.out.println("被删除的元素为:");
System.out.println(mpq.poll());
System.out.println("----------------------");
System.out.println("删除后的队列:");
for(int i:mpq.arr){
System.out.print(i+" ");
}
System.out.println();
System.out.println("----------------------");
System.out.println("插入元素后的队列:");
mpq.offer(0);
for(int i:mpq.arr){
System.out.print(i+" ");
}
System.out.println();
System.out.println("----------------------");
System.out.println("出队:");
while(mpq.size>0){
System.out.println(mpq.poll());
}
}
可以看到,出队列时,谁小谁先出。
图解:
3、堆的插入操作
1. 先将元素放入到底层空间中
2. 将最后新插入的节点向上调整,直到满足堆的性质
依次插入元素 ,如果比它的父节点小,就交换,当交换两个元素时,可能会改变堆的结构,如果交换后不符合小堆的性质,则继续交换,直到满足小堆的性质。
4、堆的删除
注意:堆的删除一定删除的是堆顶元素。具体如下:
1. 将堆顶元素对堆中最后一个元素交换
2. 将堆中有效数据个数减少一个
3. 对堆顶元素进行向下调整