优先队列是一种数据结构,它支持删除最大元素和插入元素.可以利用优先队列实现对于数组的高效排序.
数据结构二叉堆能够很好的实现优先队列的操作,在二叉堆的数组中,每个节点都大于等于它的子节点,这种情况被称为堆有序.而根节点将是堆有序的二叉树中的最大结点.在一个二叉堆中,位置k的节点的父节点的位置为[k/2],而它的两个子节点的位置则分别为2k,2K+1.这样在不使用指针的情况下,我们可以通过计算树的索引实现在树中的上下移动,a[k]向上一层就是a[k/2],向下一层就是令k等于2k或是2k+1.
下面是基于堆的优先队列的基本实现:
public class MaxPQ<Key extends Comparable<Key>> { private Key[] pq; private int N; 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); } private void swim(int k )/*在堆底插入元素的时候,所对应的上浮操作.*/ { while(k>1&&less(k/2,k)) { exch(k/2,k); k=k/2; } } public Key delMax() { Key max=pq[1]; exch(1,N--); //将其和最后一个结点交换 pq[N+1]=null; //防止对象游离 sink(1); //回复堆的有序性 return max; } private void sink(int i) { while(2*i<=N) { int j=2*i; if(j<N&&less(j,j+1)) j++; if(!less(i,j)) break; exch(i,j); i=j; } } public void exch(int i, int k) { Key t=pq[i]; pq[i]=pq[k]; pq[k]=t; } private boolean less(int i, int k) { return pq[i].compareTo(pq[k])<0; } }
对于一个N个元素的基于堆的优先级队列,插入元素操作只需不超过lgN+1次比较,删除最大元素的操作只需不超过2lgN次比较.
通过基于二叉堆的优先队列,可以实现对于数组的排序,开始的时候我们只需要扫描数组中的一半的元素,因此我们可以跳过大小为1的子堆,最后我们在位置1上调用sink方法,扫描结束.堆排序的实现算法如下:
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); } }
这段代码中,for循环构造了堆,然后while循环将最大的元素a[1]和a[N]交换,然后如此重复直到堆变空,第二阶段将堆中的最大元素删除,然后放入堆缩小后数组空出来的位置.这种算法被称为堆排序算法.将N个元素排序堆排序只需少于2NlgN+2N次比较.(2N来自于堆构造,2NlgN来自于每次下沉操作最大可能需要的比较).