优先级队列是容器的一种,可以向优先级队列中添加或取出数据,取出数据时只能取出最大的数或最小的数。而其他的一些容器比如队列和栈,取出的顺序跟插入的顺序是有关的。
优先级队列的接口如下:
public class MaxPQ<Key extends Comparable<Key>> {
MaxPQ();
void insert(Key x);
Key popMax();
boolean isEmpty();
}
优先级队列的应用
-
事件驱动的模拟
-
数字运算
-
数据压缩
-
图的查找
-
数论
-
人工智能(A*查找)
-
统计学
-
操作系统(负载平衡)
-
离散优化(背包问题)
-
垃圾邮件过滤(贝叶斯垃圾过滤)
栈和队列是优先级队列的特殊情况
问题举例
现在有一个很大的txt文件,文件中包含了许多整数,文件中的数据量非常大,无法将整个文件读取到内存中。求该文件中最大的5个整数。
解答
这个问题就要用优先级队列进行求解,每次读取一行,将数据加入到优先级队列,如果队列的长度大于5,就删除最小的元素。当整个文件读取完毕时,优先级队列中剩余的元素就是要求的最大5个数。
复杂度
设需要在N个数据中求出最大的M个元素。
-
如果用排序算法进行求解,时间复杂度是N logN,空间复杂度是N。
-
如果用基础优先级队列进行求解,时间复杂度是N logM,空间复杂度为M。
-
如果用堆的数据结构来做,那么时间复杂度是N logM,空间复杂度是M。
-
理论上能达到最小的时间复杂度是N,空间复杂度是M。
使用堆的算法来求解该问题时,它的复杂度已经非常接近理论极限了。
代码
下面展示了最简单的优先级队列实现方法。插入的复杂度是1,删除的复杂度是N。
public class UnorderedPQ<Key extends Comparable<Key>> {
private Key[] items;
private int N;
public UnorderedPQ(int capacity) {
items = (Key[])new Comparable[capacity];
}
public void insert(Key item) {
items[N] = item;
N++;
}
public Key popMax() {
// 找出最大的数字
int max = 0;
for(int i=1;i<N;i++){
if(SortUtil.less(items[max], items[i])) {
max=i;
}
}
// 删除最大的数字
Key result = items[max];
SortUtil.exch(items, max, N-1); // 注意:这里是N-1,不是N
N--;
// 返回最大的数
return result;
}
public boolean isEmpty() {
return N==0;
}
}