支持两种操作:删除最大元素和插入元素的数据类型叫做优先队列。
队列:删除最老的元素。
栈:删除最新的元素。
基于二叉堆数据结构的一种优先队列的经典实现方法,用数组保存元素并按照一定条件排序。
数据结构二叉堆能够很好地实现优先队列的基本操作。在二叉堆的数组中,每个元素都要保证大于等于另两个特定位置的元素。相应地,这些位置的元素又至少要大于等于数组中的另两个元素。
当一个二叉树的每个结点都大于等于它的两个子结点时,它被称为堆有序。根节点是堆有序的二叉树中的最大结点。
二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级储存(不适用数组的第一个位置)。
基于堆的优先队列
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;
}
}
}
优先队列由一个基于堆的完全二叉树表示,存储于数组pq[1..N]中,pq[0]不使用。若含有N个元素,则插入元素操作只需不超过(lgN+1)次比较,删除最大元素操作需要不超过2lgN次比较。
堆排序
堆排序分为两个阶段。在堆的构造阶段,将原始数据重新组织进一个堆中;然后在下沉排序阶段,我们从堆中按递减顺序取出所有元素并得到排序结果。
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);
}
}
用sink()方法首先将数组里的元素排序,for循环构造了堆,然后while循环将最大的元素a[1]和a[N]交换,N值减一并修复新堆,如此重复直到堆变空。