堆排序 HeapSort
二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级存储(不适用数组的一个位置,下标从1开始)!
二叉堆可以很好的实现优先队列的基本操作。优先队列是一种抽象的数据结构,主要有insert(插入元素) 和 delMax(删除最大操作)两个标志性操作。在一个堆中,位置k的节点的父节点的位置是是k/2,而他的两个子节点的位置则分别是2k,2k+1!利用数组中无需指针即可沿树上下移动的遍历,算法可以保证对数复杂度的性能。
堆的操作会首先进行一些简单的改动,打破堆的状态,然后再遍历堆并按照要求将对的状态恢复,这个过程被称为堆的有序化!
堆的有序化
由下至上的堆有序化(上浮)
如果堆的有序化因为某个节点变得比父节点而被打破,将它和父节点交换来修复堆。当这个节点可能比父节点更大,将这个节点不断上移直到我们遇到更大的父节点!也就是说当该结点不再大于它的父节点,堆的有序状态就恢复了!
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;
//j保存较大子节点的索引
if(j<N&&less(j,j+1)) j++;
//如果该结点比子结点都大,则终止循环
if(!less(k,j)) break;
//和较大子节点交换
exch(k,j);
//下一层有序化
k=j;
}
}
Insert 插入元素
将新元素插入到数组末尾,增加堆的大小,并让这个新元素上浮到合适的位置!
DelMax 删除最大元素
从数组的顶端删除最大元素,并将数组最后一个元素放到顶端,缩小堆的大小并让这个元素下沉到合适位置!
基于堆的优先队列
package DataStruct;
public class MaxHeapPQ<T extends Comparable<T>> {
// 基于堆的完全二叉树
private T[] pq;
// 存储在pq[1....N]中,pq[0]不适用
private int N = 0;
// 插入元素
public void insert(T t) {
pq[++N] = t;
swim(N);
}
// 删除最大元素
public T delMax() {
// 从根结点的得到最大元素
T max = pq[1];
// 将其和最后一个元素交换
exch(1, N--);
// 放置对象游离,有利于回收
pq[N + 1] = null;
// 下沉,恢复堆的有序性
sink(1);
return max;
}
// 上浮
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;
// j保存较大子节点的索引
if (j < N && less(j, j + 1))
j++;
// 如果该结点比子结点都大,则终止循环
if (!less(k, j))
break;
// 和较大子节点交换
exch(k, j);
// 下一层有序化
k = j;
}
}
// 小于
private boolean less(int i, int j) {
if (pq[i].compareTo(pq[j]) < 0)
return true;
else
return false;
}
// 交换函数
private void exch(int i, int j) {
T temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
}
}
堆排序
堆排序可以分为两个阶段:构建堆(将原始数组重新组织放到一个堆中),然后下沉排序,从堆中按照递减的顺序取出所有的元素并得到排序的结果!
堆的构建
从右到左用sink()函数构造子堆,开始是我们只需要扫描数组一般的元素(N/2),因为我们可以跳过大小为1的子堆
/*堆排序*/
public static void HeapSort(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);
}
}
下沉排序
将堆中最大的元素删除,然后放入堆缩小后数组空出的位置。