[轉載] 《算法導論》和 百度百科
1、堆的定義
(二叉)堆是一個數組,它可以被看成一個近似的完全二叉樹。樹上的每一個節點對應數組中的一個元素。除最底層(葉子層)外其它層都是滿的。
規定樹的根結點是A[1],這樣給定一個節點的下標i,我們可以 很容易計算它的父節點、左孩子、右孩子的下標。
PARENT(i)=i/2;
LEFT(i)=2*i;
RIGHT(i)=2*i+1;
(二叉)堆可分爲兩種形式:最大堆和最小堆。
最大堆要滿足如下性質:
A[PARENT(i)]>=A[i]
即除了根節點以外的所有節點,其父節點的值要大於等於該節點的值。
最小堆是滿足如下性質:
A[PARENT(i)]<=A[i]
在從小到大的堆排序中,我們使用最大堆。
2、堆排序的思想
2.1、維護最大堆的性質
2.2、建堆
2.3、堆排序算法
3、堆排序的實現
#include <stdio.h>
//維護最大堆的性質
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;
//建堆
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[]={9,8,7,6,5,4,3,2,1,0};
HeapSort(num,sizeof(num)/sizeof(int));
for(i=0;i<sizeof(num)/sizeof(int);i++)
{
printf("%d ",num[i]);
}
printf("\nok\n");
return 0;
}
4、堆排序的性能
建堆時間複雜度是O(n),堆排序是O(nlgn)。所以總的時間複雜度是O(nlgn)。
和其它排序算法的比較:
算法思想類似於選擇排序,而且也是不穩定的排序算法。因爲簡單選擇排序每次通過比較找到最大元素,堆排序是通過維護最大堆的性質在根節點找到最大元素,然後放到數組最後,直到所有節點都排好序。既然類似選擇排序,那也會影響等值元素在數組中的原有順序,從而破壞穩定性。