在谈优先级队列之前先简单说一下堆的概念。
一、概念
1.堆在逻辑上是一棵完全二叉树
2.物理上是保存在数组中
3.满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
4.反之,则是小堆,或者小根堆,或者最小堆
5.堆的基本作用是,快速找集合中的最值
二、大小根堆的建立向下调整实现
调整过程:
1.判断 index 位置有没有孩子
1)index 如果已经是叶子结点,则整个调整过程结束
2)因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子
3)因为堆的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即 left >= size 越界
2.确定 left 或 right,谁是 index 的最小孩子 min
1)如果右孩子不存在,则 min = left
2)否则,比较 array[left] 和 array[right] 值得大小,选择小的为 min
3.比较 array[index] 的值 和 array[min] 的值,如果 array[index] <= array[min],则满足堆的性质,调整结束
4.否则,交换 array[index] 和 array[min] 的值
5.然后因为 min 位置的堆的性质可能被破坏,所以把 min 视作 index,向下重复以上过程
//向下调整实现大根堆和小根堆
import java.util.Arrays;
public class ShiftDown {
public static void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void shiftDownBig(int[] arr, int length, int parent){//向下调整大根堆
int child = parent * 2 + 1;//左孩子下标
while(child < length){
if(child + 1< length && arr[child + 1] > arr[child]){//如果右孩子存在并且值大于左孩子,要比较的就是右孩子,更新child
child++;
}
if(arr[child] > arr[parent]){//如果child位置的值大于parent位置的值
swap(arr, child, parent);//交换两个值
parent = child;//继续向下调整,更新parent和child的值
child = parent * 2 + 1;
}else{
break;
}
}
}
public static void shiftDownSmall(int[] arr, int length, int parent){//向下调整小根堆
int child = parent * 2 + 1;//左孩子下标
while(child < length){
if(child + 1 < length && arr[child + 1] < arr[child]){
child++;
}
if(arr[child] < arr[parent]){
swap(arr, child, parent);
parent = child;
child = parent * 2 + 1;
}else{
break;
}
}
}
public static void creatBigHeap(int[] arr){//建立大根堆
for(int i = (arr.length - 2) / 2; i >= 0; i--){//从最后一个非叶子节点开始向下调整建立大根堆
shiftDownBig(arr, arr.length, i);
}
}
public static void creatSmallHeap(int[] arr){//建立小根堆
for(int i = (arr.length - 2) / 2; i >= 0; i--){//从最后一个非叶子节点开始向下调整建立小根堆
shiftDownSmall(arr, arr.length, i);
}
}
public static void main(String[] args) {
int[] arr = {1, 4, 5, 3, 19, 22, 2};
int[] arr1 = arr.clone();
creatBigHeap(arr);
System.out.println(Arrays.toString(arr));
creatSmallHeap(arr1);
System.out.println(Arrays.toString(arr1));
}
}
优先级队列是一种特别的队列,是一种按照对象优先级进行数据处理的一种数据结构,每次处理都会最先处理优先级最高的对象。有两个最基本的操作,一个是返回最高优先级对象(出队列),一个是添加新的对象(入队列)。优先级队列不允许为null。
一、优先级队列的使用
Java中使用优先级队列PriorityQueue对对象进行优先级排序需要实现Comparable或Comparator接口用来给对象排序,不实现接口会默认按照自然顺序排序(小根堆)。
1.创建小根堆:
import java.util.Comparator;
import java.util.PriorityQueue;
class compareQueue implements Comparator{
@Override
public int compare(Integer a, Integer b){
return a - b;
}
}
public class MyPriorityQueue {
public static void main(String[] args) {
PriorityQueue queue = new PriorityQueue<>(new compareQueue());
queue.offer(5);
queue.offer(6);
queue.offer(4);
queue.offer(3);
queue.offer(7);
queue.offer(2);
System.out.println(queue);
}
}
2.创建大根堆:
import java.util.Comparator;
import java.util.PriorityQueue;
public class MyPriorityQueue {
public static void main(String[] args) {
PriorityQueue queue = new PriorityQueue<>(new Comparator(){
@Override
public int compare(Integer a, Integer b) {
return b - a;
}
});
queue.offer(5);
queue.offer(6);
queue.offer(4);
queue.offer(3);
queue.offer(7);
queue.offer(2);
System.out.println(queue);
}
}
二、优先级队列的实现
一般用堆来实现优先级队列
1.入队列
以大根堆为例:
1.首先按尾插方式放入数组
2.比较其和其父亲的值的大小,如果父亲的值大,则满足堆的性质,插入结束
3.否则,交换其和父亲位置的值,重新进行 2、3 步骤
4.直到根结点
2.出队列
出队列出来的元素是优先级最高的。其中为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过向下调整方式重新调整成堆。
代码实现
//优先级队列入队、出队、获取队首元素、判空操作的实现
import java.util.Arrays;
import java.util.PriorityQueue;
public class MyPriorityQueue {
private int[] arr = new int[10];
private int size = 0;
public void shiftUpBig(int[] arr, int child){//大根堆向上调整
int parent = (child - 1) / 2;
while(child > 0) {
if (arr[child] > arr[parent]) {
ShiftDown.swap(arr, child, parent);
child = parent;
parent = (child - 1) / 2;
} else {
break;
}
}
}
public void shiftUpSmall(int[] arr, int child){//小根堆向上调整
int parent = (child - 1) / 2;
while(child > 0) {
if (arr[child] < arr[parent]) {
ShiftDown.swap(arr, child, parent);
child = parent;
parent = (child - 1) / 2;
} else {
break;
}
}
}
public void offer(int value){//入队
if(size == arr.length){//扩容
Arrays.copyOf(arr, arr.length * 2);
}
arr[size++] = value;//尾插
shiftUpSmall(arr, size - 1);//向上调整
}
public int poll(){//出队
if(size > 0){//如果队中有元素
int poll = arr[0];//记录队首元素
ShiftDown.swap(arr, 0, size - 1);//交换队首与队尾元素
size--;//删除队尾元素
ShiftDown.shiftDownSmall(arr, size, 0);//将队首元素调整下去
return poll;//返回记录的队首元素
}
return -1;
}
public int peek(){//获取队首元素
if(size > 0){
return arr[0];//返回队首元素
}
return -1;
}
public boolean isEmpty(){//判断队列是否为空
return size == 0;
}
public static void main(String[] args) {
PriorityQueue pq = new PriorityQueue<>();
pq.offer(9);
pq.offer(2);
pq.offer(10);
pq.offer(15);
pq.offer(3);
pq.offer(1);
pq.offer(7);
pq.offer(23);
pq.offer(89);
while(!pq.isEmpty()){
System.out.print(pq.poll() + " ");
}
System.out.println();
System.out.println("My PriorityQueue");
MyPriorityQueue mpq = new MyPriorityQueue();
mpq.offer(9);
mpq.offer(2);
mpq.offer(10);
mpq.offer(15);
mpq.offer(3);
mpq.offer(1);
mpq.offer(7);
mpq.offer(23);
mpq.offer(89);
while(!mpq.isEmpty()){
System.out.print(mpq.poll() + " ");
}
System.out.println();
}
}