堆是一種完全二叉樹,分爲小根堆和大根堆。大根堆中任意節點的值都不大於其父節點的值。有些類似於二叉搜索樹,都是要求父節點和子節點的值滿足某種關係。
堆排序的基本思想是,先用所給數據建立大根堆,堆頂元素即爲最大值,將其與最後一個元素交換,此時最後一個元素爲最大值;再對最大值之前的元素進行調整,使其恢復爲大根堆。再如此將次大值取出排列在最大值之前,循環往復,直到所有元素都排好序。
於是,排序的關鍵就是建堆和調整。如下HeapAjust函數實現調整樹的功能,使其滿足堆的性質。算法是:從一個指定節點開始,如果某個節點的值小於其子節點的值,就將其與較大的子節點交換,以使當前父節點與子節點滿足堆的性質;然而,進行調整後,以被交換的子節點爲根的子樹可能又不滿足堆的性質了,因此需要繼續向下迭代遍歷,直到當前節點滿足堆的性質,就退出循環。
這是調整函數,想要建立堆,需要從最後一個非葉節點開始,自底向上調用此函數。堆排序時,每次取出堆中的最大元素後,要對剩下的元素進行調整,以維持堆的性質。直到所有節點都已取出,完成排序。
這是百度百科中調整函數、堆排序函數及測試代碼:
#include <stdio.h>
//array是待調整的堆數組,i是待調整的數組元素的位置,nlength是數組的長度
//本函數功能是:調整以第i個節點(即值爲array[i]的節點)爲根節點的樹,使其成爲大根堆
void HeapAdjust(int array[], int i, int nLength)
{
int nChild;
int nTemp;
for(; 2*i+1 < nLength; i=nChild)
{
//子結點的位置=2*(父結點位置)+1
nChild = 2*i+1;
//得到子結點中較大的結點
if(nChild < nLength-1 && array[nChild+1] > array[nChild])
++nChild;
//如果較大的子結點大於父結點那麼把較大的子結點往上移動,替換它的父結點
if(array[i] < array[nChild])
{
nTemp=array[i];
array[i]=array[nChild];
array[nChild]=nTemp;
}
else
break; //否則退出循環
}
}
//堆排序算法
void HeapSort(int array[], int length)
{
int i, j;
//建堆
//調整序列的前半部分元素,調整完之後第一個元素是序列的最大的元素
//length/2-1是最後一個非葉節點,此處"/"爲整除
for(i=length/2-1; i>=0; --i)
{
HeapAdjust(array, i, length);
}
//從最後一個元素開始對序列進行調整,不斷的縮小調整的範圍直到第一個元素
for(i=length-1; i>0; --i)
{
//把第一個元素和當前的最後一個元素交換,
//保證當前的最後一個位置的元素都是在現在的這個序列之中最大的
array[i] = array[0]^array[i];
array[0] = array[0]^array[i];
array[i] = array[0]^array[i];
//不斷縮小調整heap的範圍,每一次調整完畢保證第一個元素是當前序列的最大值
HeapAdjust(array,0,i);
}
}
int main()
{
int i;
int num[]={0,1,2,3,4,5,6,7,8,9};
HeapSort(num, sizeof(num)/sizeof(int));
for(i=0; i < sizeof(num)/sizeof(int); i++)
{
printf("%d ",num[i]);
}
printf("\n");
return 0;
}