进行堆排序之前,先介绍一下堆。一般所说的堆指的是二叉堆,它的使用对于优先队列的实现相当普遍。
具体来讲,堆是一棵完全二叉树,其有一个极其重要的性质就是:我们可以使用数组来表示一个堆,对于数组中的任何一个元素下标i,i/2对应的是其父节点,2*i对应的是其左孩子,2*i+1对应的是其右孩子。根节点是从下标1开始。这样的性质决定了我们可以方便的使用数组来构建一个堆。
堆的非常明显一个特点就是:所有的父节点都大于(称为大堆)或小于(称为小堆)子节点。也就是说,一个堆的顶点不是所有节点的最小值,就是所有节点的最大值。
以上的两个堆的特点使利用堆排序称为可能。在介绍堆排序之前先介绍堆的两种基本操作。
构建一个堆(利用上滤插入操作)
删除根节点(利用下滤删除操作)
insert操作:
利用上滤(percolate up)的方法,将要插入的位置设为hole,逐层比较,直到找到插入位置。
deteteMin操作:
利用下滤(percolate down)方法,这里假设是小堆,我们进行下滤时,首先将根节点元素删除,然后将堆的最后一个元素作为要移动的元素,并将起始插入位置hole设为根节点。
逐层比较子节点和最后一个元素,如果子节点小,就将子节点上移到根节点,并将hole设为当前子节点。这里需要注意的一个问题是需要先找到左右子节点中较小的那一个再进行和要插入的元素比较,才能确保每次上移到根节点的子节点一定是最小的。最后找到插入位置,将最后一个元素插入到该位置再删除最后一个元素即可。
利用这两种操作,构建堆可使用连续的insert,堆排序可使用deleteMin并将删除的最小元素放到已经删除的最后一个元素的位置,这样做的好处是节省内存空间,对于小堆来说,进行此种排序的结果是最后的数组是从大到小排列的,大堆反之。
下面贴上代码和运行结果:
(java实现)
/*
* 有关堆heap的两种操作,也是堆排序的基础
*/
public class HeapOperation {
/**
* @param args
*/
int[] a;
int elementNumber;
//构造函数
public HeapOperation()
{
//利用数组表示一个堆
elementNumber = 0;
a = new int[elementNumber];
}
//插入操作,利用上滤(percolate up)
public void insert(int x)
{
while(a.length <= elementNumber+1)
enlargeArray(2*a.length+1);
//进行上滤
int hole = ++elementNumber;
for(; hole>1 && a[hole/2] > x ; hole/=2)
a[hole] = a[hole/2];
a[hole] = x;
}
//删除最小元,利用下滤操作(percolate down)
public void deleteMin()
{
//先将最后一个元素,也就是a[elementNumber] 移到最小元a[1]处,并将要插入的位置hole放到根起点,
//进行下滤
a[1] = a[elementNumber];
int hole = 1;
percolateDown(hole);
}
public int popMin()
{
int min = a[1];
deleteMin();
return min;
}
private void percolateDown(int hole)
{
int value = a[hole];
for(int child = hole*2;hole*2 < elementNumber ; hole = child)
{
//找出hole的子节点中较小的那个和其比较
child = hole*2;
if( a[child] > a[child+1] )
child++;
//要下滤的值和较小子节点比较
if(a[child] < value)
a[hole] = a[child];
}
a[hole]= value;
a[elementNumber]=0;
elementNumber--;
}
private void enlargeArray(int length)
{
int [] tmp = a;
a = new int[length];
for(int i=0;i<tmp.length;i++)
a[i] = tmp[i];
}
public void buildHeap(int elementNumber)
{
for(int i = 0; i< elementNumber ; i++)
this.insert((int) (Math.random()*100));
}
public void heapSort(int[] a,int elementNumber)
{
for(int i= elementNumber; i > 0 ; i--)
{
a[i] = popMin();
}
}
public static void main(String[] args)
{
HeapOperation ho = new HeapOperation();
int recycleTimes = 20;
ho.buildHeap(recycleTimes);
System.out.println("随机建立的长度为"+recycleTimes+"的堆:");
for(int i=0; i< ho.a.length; i++)
System.out.print(ho.a[i]+" ");
System.out.println();
ho.deleteMin();
System.out.println("删除一个最小元后的堆:");
for(int i=0; i< ho.a.length; i++)
System.out.print(ho.a[i]+" ");
System.out.println();
System.out.println("堆排序结果:");
ho.heapSort(ho.a, ho.elementNumber);
for(int i=0; i< ho.a.length; i++)
System.out.print(ho.a[i]+" ");
}
}
随机建立的长度为20的堆:
0 1 3 7 6 39 35 11 41 13 44 44 96 52 73 76 95 46 51 41 90 0 0 0 0 0 0 0 0 0 0
删除一个最小元后的堆:
0 3 6 7 13 39 35 11 41 41 44 44 96 52 73 76 95 46 51 90 0 0 0 0 0 0 0 0 0 0 0
堆排序结果:
0 96 90 76 52 51 46 46 44 41 41 41 39 35 13 11 11 7 6 3 0 0 0 0 0 0 0 0 0 0 0