本篇将介绍优先级队列,并自己创建一个。
目录
概念
首先注意:堆只是优先级队列的一种是实现方式而已
讲到优先级队列就要讲到二叉树
使用数组存储,并用层序遍历的方式放入数组中就是它的表现形式
一般为了保证没有空间浪费,存储的都是完全二叉树
分类
优先级队列还分为大根堆和小根堆
大根堆就是任意节点值都大于子树节点值,小根堆相反
因为它们的特点,所以优先级队列的作用就是快速找到最值。
创建优先级队列
这里我们创建一个大根堆,但注意Java库中的优先级队列是小根堆
基本方法
基本的方法如下,下面一一讲解:
public class MyHeap {
public int[] elem;
public int usedSize;
public MyHeap(){
}
public void shiftDown(int parent, int len){
}
public void shiftUp(int child){
}
public void offer(int val){
}
public boolean isFull(){
}
public int poll(int val){
}
public boolean isEmpty(){
}
}
首先说构造方法,这里我们先创建一个数组。
public MyHeap(){
this.elem = new int[10];
}
再完成offer和poll的方法
offer方法首先判满,满了就扩容,把数据放到数组尾端,然后开始调整二叉树成为大根堆
因为是在尾端插入,所以向上调整,需要一个 shiftUp() 方法
public void offer(int val){
if (isFull()){
Arrays.copyOf(this.elem, this.elem.length * 2);
}
elem[usedSize++] = val;
shiftUp(usedSize - 1);//这里减1是因为上面++了,usedSize指向的是插入值的位置
}
public boolean isFull(){
return this.elem.length == usedSize;
}
poll方法需要判空,空就报异常,然后分两步
- 第一步把第一个值和最后一个值交换
- 第二步从0开始向下调整堆
不同于offer,我们需要一个shiftDown()方法来向下调整堆
public int poll(int val){
if (isEmpty()){
throw new RuntimeException("异常");
}
//交换最后一个值和第一个值
int tmp = elem[0];
elem[0] = elem[usedSize - 1];
elem[usedSize - 1] = tmp;
usedSize--;
//向下调整堆
shiftDown(0, usedSize);
return tmp;
}
public boolean isEmpty(){
return usedSize == 0;
}
shiftDown()方法
此方法用于向下调整堆,在poll时使用
因为向下调整,所以需要一个参数表示父亲节点,一个参数表示终止点
有了父亲节点就可以知道孩子节点的位置:
child = parent * 2 + 1
只要孩子节点没到终止点就一直进行判断调整(while)
此时有两种情况:最后一个节点这里有没有右孩子
如果没有右孩子,直接判断孩子和父亲节点值的大小
如果有右孩子,先判断左右孩子大小再判断和父亲节点大小值
最后继续向下调整即可
public void shiftDown(int parent, int len){
int child = parent * 2 + 1;
while (child < len){
//看是否有右孩子
if (child + 1 < len && elem[child] < elem[child + 1]){
child++;//左边小就用右孩子和父亲节点比
}
if (elem[child] > elem[parent]){
int tmp = elem[parent];
elem[parent] = elem[child];
elem[child] = tmp;
//交换完成后要继续向下看是否还可以调整
parent = child;
child = parent * 2 + 1;
}else {
//如果父亲节点都是大根堆,那么孩子一定是大根堆
break;
}
}
}
shiftUp()方法
理解了shiftDown()方法,再写shiftUp就简单很多了。
由于此方法是向上调整,所以参数是孩子节点
有了孩子节点就可以知道父亲节点:
parent = (child - 1) / 2
只要孩子节点大于0就一直向上调整(while)
向上调整比较简单,直接进行判断,孩子大于父亲就交换,不大于说明插入值小,直接退出即可
public void shiftUp(int child){
int parent = (child - 1) / 2;
while (child > 0){
//直接判断,因为只针对插入值
if (elem[child] > elem[parent]){
int tmp = elem[parent];
elem[parent] = elem[child];
elem[child] = tmp;
child = parent;
parent = (child - 1) / 2;
}else {
break;
}
}
}
最后说明创建堆的时间复杂度是O(N),原因如下:
关于堆的故事还有很多,未完待续。。。