/**
* @author qcg
* @version 2019/5/16.
* @description 优先队列,二叉堆是实现优先队列的基础。该例中二叉堆属于大堆
* 应用场景:
* 1.topK问题
* 2.不需要FIFO按照权重操作出队的情况
* 3.RabbitMQ中,当消费者不足,不能及时进行消费的情况下,优先级队列会生效
* 4.hadoop中Map结束之后会将IFile文件排序合并成一个大文件,这里的排序是基于堆实现的优先级队列
*/
public class MyPriorityQueue {
private int size;
private int[] array;
public MyPriorityQueue() {
// 初始队列长度为16
array = new int[16];
}
/**
* 入队
*
* @param key 入队元素
*/
public void enQueue(int key) {
// 队列长度超出范围,扩容
if (size >= array.length) {
resize();
}
array[size++] = key;
upAdjust();
}
/**
* 出队
*
* @return 队中最大元素
* @throws Exception
*/
public int deQueue() throws Exception {
System.out.println("下沉前array:" + Arrays.toString(array) + " size:" + size);
if (size < 0) {
throw new Exception("the queue is empty!");
}
// 获取堆顶元素
int head = array[0];
// 让最后一个元素移动到堆顶,实际没有删除元素的值,只是将size--了
array[0] = array[--size];
downAdjust();
System.out.println("下沉后array:" + Arrays.toString(array) + " size:" + size);
return head;
}
/**
* "上浮"调整
*/
private void upAdjust() {
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;导致死循环
childIndex = 2 * childIndex + 1;
}
array[parentIndex] = temp;
}
/**
* 队列扩容
*/
private void resize() {
// 队列容量翻倍
int newSize = this.size * 2;
this.array = Arrays.copyOf(this.array, newSize);
}
public static void main(String[] args) throws Exception {
MyPriorityQueue priorityQueue = new MyPriorityQueue();
priorityQueue.enQueue(3);
priorityQueue.enQueue(5);
priorityQueue.enQueue(10);
priorityQueue.enQueue(2);
priorityQueue.enQueue(7);
System.out.println("出队元素:" + priorityQueue.deQueue());
System.out.println("出队元素:" + priorityQueue.deQueue());
}
}
注意parentIndex和childIndex移动的规律,以及size的变化