简介
许多应用程序都需要处理有序的元素,但不一定要求它们全部有序,或者不一定要一次就将它们排序。很多情况下我们会收集一些元素,处理当前键值最大的元素,然后再收集更多的元素,再处理当前键值最大的元素。在这种情况下,一个合适的数据结构应该支持两种操作:删除最大元素和插入元素。这种数据结构类型叫做优先队列。
堆的算法
下面给出基于堆的优先队列。
public class MaxPQ<Key extends Comparable<Key>> {
private Key[] pq; // 基于堆的完全二叉树
private int N = 0; // 存储与pq[1..N]中,pq[0]没有使用
public MaxPQ(int maxN) {
pq = (Key[]) new Comparable[maxN + 1];
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
public void insert(Key v) {
pq[++N] = v;
swim(N);
}
public Key delMax() {
Key max = pq[1]; // 从根结点得到最大元素
exch(1, N--); // 将其和最后一个结点交换
pq[N + 1] = null; // 防止对象游离
sink(1); // 恢复堆的有序性
return max;
}
private boolean less(int i, int j) {
return pq[i].compareTo(pq[j]) < 0;
}
private void exch(int i, int j) {
Key t = pq[i];
pq[i] = pq[j];
pq[j] = t;
}
private void swim(int k) {
while(k > 1 && less(k/2, k)) {
exch(k/2, k);
k = k/2;
}
}
private void sink(int k) {
while(2 * k <= N) {
int j = 2 * k;
if (j < N && less(j, j + 1)) {
j++;
}
if (!less(k, j)) {
break;
}
exch(k, j);
k = j;
}
}
}
下面是使用优先队列的多向归并。
public class Multiway {
public static void merge(In[] streams) {
int N = streams.length;
IndexMinPQ<String> pq = new IndexMinPQ<String>(N);
for (int i = 0; i < N; i++) {
if (!streams[i].isEmpty()) {
pq.insert(i, streams[i].readString());
}
}
while(!pq.isEmpty()) {
StdOut.println(pq.min());
int i = pq.delMin();
if (!streams[i].isEmpty()) {
pq.insert(i, streams[i].readString());
}
}
}
public static void main(String[] args) {
int N = args.length;
In[] streams = new In[N];
for (int i = 0; i < N; i++) {
streams[i] = new In(args[i]);
}
merge(streams);
}
}
堆排序
堆排序可以分为两个阶段。在堆的构造阶段中,我们将原始数组重新组织安排进一个堆中;然后在下沉排序阶段,我们从堆中按递减顺序取出所有元素并得到排序结果。
public static void sort(Comparable[] a) {
int N = a.length;
for (int k = N / 2; k >= 1; k--) {
sink(a, k, N);
}
while(N > 1) {
exch(a, 1, N--);
sink(a, 1, N);
}
}