1.优先队列是一个数据结构,队列中的记录是有关键字的,它支持两个基本操作:插入新记录和删除关键字最大的记录。
2.堆排序,如果一棵树中每个节点中的关键字都大于或等于所有子节点中的关键字,就称它为按堆排序。
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。完全二叉树中,第i个元素是第2i个元素和第2i+1个元素的父亲。
堆是一个节点集合,其中的关键字排放在一个堆排序的完全二叉树中,表示为一个数组。
3.自底向上堆化
当一个节点的优先级提高了,为了重构堆,我们从堆的下方不断往上移动,交换节点k和它的父节点(k/2)。如果需要,继续交换直到a[k/2]<a[k]或者直到我们到达堆的顶部。
1 template <class Item> 2 void fixUp(Item a[], int k) 3 { 4 while ( k > 1 && a[k/2] < a[k]) 5 { 6 exch(a[k], a[k/2]); 7 k = k/2; 8 } 9 }
4.自顶向下堆化
当一个节点的优先级下降后,为了重构,我们从堆的上部向下移动,如果必要的话,把节点k和它的较大节点互换,直到在位置k的节点的子节点都小于k节点或者k节点已经在堆的底部。注意,如果N是偶数而k=N/2,那么在位置k的节点只有一个子节点——这种情况必须适当处理。
在这个程序里的内部循环由两个特殊出口:一个是到达了堆的底部,另一个是在堆的内部已经符合堆的条件了。这是一个“break”结构的原型例子。
1 template <class Item> 2 void fixDown(Item a[], int k, int N) 3 { 4 while (2*k <= N) 5 { 6 int j = 2*k; 7 if (j < N && a[j] < a[j+1]) j++; 8 if (!(a[k] < a[j])) break; 9 exch(a[k], a[j]); 10 k = j; 11 } 12 }
5.基于堆的优先队列
为了实现insert操作,我们把N增加1,把最新的元素加入到堆的末尾,然后用fixUp操作来重构堆。对于getmax操作,堆的大小要减1,然后我们得到pq[1]的值,再把pq[N]移到pq[1]来减少堆的大小,接着用fixDown操作重构堆。数组的第一个位置pq[0]是不用的,但可以作为一些实现的标志。
1 template <class Item> 2 class PQ 3 { 4 private: 5 Item *pq: 6 int N; 7 public: 8 PQ(int maxN) 9 {pq = new Item[maxN+1]; N = 0;} 10 int empty() const 11 {return N == 0;} 12 void insert(Item item) 13 {pq[++N] = item; fixUp(pq, N);} 14 Item getmax() 15 { 16 exch(pq[1], pq[N]); 17 fixDown(pq, 1, N-1); 18 return pq[N--]; 19 } 20 };