(参考算法第四版)
优先队列:
定义:
一个合适的数据结构支持两种操作:删除最大元素和插入元素。这种数据类型叫做优先队列。
-
插入元素:我们将新元素加到数组末尾,增加堆的大小并让这个新元素上浮到合适的位置。
-
删除最大元素:我们从数组顶端删去最大的元素将数组的最后一个元素放在顶端,减小堆的大小并让这个元素下沉到合适的位置。
应用:
许多应用程序都需要处理有序的元素,但不一定要求它们全部有序,或许不一定要一次就将它们排序。许多情况下我们会收集一些元素,处理当前键值最大的元素,然后再收集更多的元素,再处理当前键值最大的元素,如此这般。例如,你可能有一台能够同时运行多个应用程序的电脑(或者手机)。这是通过为每个应用程序的事件分配一个优先级,并总是处理下一个优先级最高的事件来实现。例如,绝大多数手机分配给来电的优先级都会比游戏程序的高。
实现:
这里主要使用堆来实现优先队列,因为数据结构二叉堆能够很好的实现优先队列的基本操作。在二叉堆数组中,每个元素都要保证大于等于另两个特定位置的元素。相应地,这些位置的元素有至少要大于等于数组中的另两个元素。以此类推。这也称为堆有序。
-
堆有序:当一棵二叉树的每个结点都大于等于它的两个子结点时,它称为堆有序。
堆算法:
分为上浮和下沉
-
上浮:由上至下的堆有序化。如果堆的有序状态因为某个结点变得比它的父结点更大而被打破,那么我们就需要通过交换它和它的父结点来修复堆。
-
下沉:由下至上的堆有序化。如果堆的有序状态因为某个结点变得比它的俩个子结点或是其中之一更小了而被打破,那么我们就需要通过将它和它的两个子结点中的较大者交换来修复堆。
具体的编程如下:
package com.zhihuangcao.maxpq;
/**
* @author CaoZhihuang
* @describe 最大堆优先队列
*/
public class MaxPriorityQueue {
private static int N = 0;
private static int[] pq;
// 构造函数,初始化pq数组
public MaxPriorityQueue(int init_N) {
pq = new int[init_N];
}
public static void main(String[] args) {
int array[] = new int[] { 5, 2, 4, 6, 2, 3, 7, 4 };
new MaxPriorityQueue(array.length);
for (int i = 0; i < array.length; i++) {
insert(array[i]);
}
System.out.println("删除的数值是:" + delMaxElement());
System.out.print("遍历数组值是:");
for (int i : pq) {
System.out.print(i + " ");
}
}
/**
* @methor 向堆中插入新数值
* @return void
* @param val
*/
private static void insert(int val) {
pq[N++] = val;
swim(N - 1);
}
/**
* @methor 上浮(由下至上的堆有序化)
* @return void
* @param n
*/
private static void swim(int n) {
while (n > 0 && less((n - 1) / 2, n)) {
Exchange((n - 1) / 2, n);
n = (n - 1) / 2;
}
}
/**
* @methor 删除堆中的最大值,即删除堆顶
* @return val
*/
private static int delMaxElement() {
int val = pq[0]; // 从根结点得到最大元素
Exchange(0, N - 1); // 将最大元素与最后一个结点交换
N--; // 将N减一
pq[N] = 0; // 将交换后的最后一个结点赋值0
sink(0); // 下沉,恢复堆的有序性
return val; // 返回最大元素
}
/**
* @methor 下沉(由上至下的堆有序化)
* @return void
* @param n
*/
private static void sink(int n) {
while (2 * n + 1 < N) {
int childN = 2 * n + 1;
if (less(childN, childN + 1))
childN++;
if (less(n, childN)) {
Exchange(n, childN);
n = childN;
}
// 注意这里要跳出循环
else
break;
}
}
/**
* @methor 交换数值
* @return void
* @param n1
* @param n2
*/
private static void Exchange(int n1, int n2) {
int val = 0;
val = pq[n1];
pq[n1] = pq[n2];
pq[n2] = val;
}
/**
* @methor 比较两个数值
* @param n1
* @param n2
* @return boolean
*/
private static boolean less(int n1, int n2) {
return pq[n1] < pq[n2];
}
/**
* @methor 判空
* @param N
* @return boolean
*/
private static boolean isEmpty(int N) {
return N == 0;
}
}