堆排序

      堆排序是利用堆的性質進行的一種選擇排序。下面先討論一下堆。

1.堆

  堆實際上是一棵完全二叉樹,其任何一非葉節點滿足性質:

  Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]

  即任何一非葉節點的關鍵字不大於或者不小於其左右孩子節點的關鍵字。

  堆分爲大頂堆和小頂堆,滿足Key[i]>=Key[2i+1]&&key>=key[2i+2]稱爲大頂堆,滿足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]稱爲小頂堆。由上述性質可知大頂堆的堆頂的關鍵字肯定是所有關鍵字中最大的,小頂堆的堆頂的關鍵字是所有關鍵字中最小的。

2.堆排序的思想

   利用大頂堆(小頂堆)堆頂記錄的是最大關鍵字(最小關鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。

    其基本思想爲(大頂堆):

    1)將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆爲初始的無序區;

    2)將堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序區(R1,R2,......Rn-1)和新的有序區(Rn),且滿足R[1,2...n-1]<=R[n]; 

    3)由於交換後新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,......Rn-1)調整爲新堆,然後再次將R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排序過程完成。

    操作過程如下:

     1)初始化堆:將R[1..n]構造爲堆;

     2)將當前無序區的堆頂元素R[1]同該區間的最後一個記錄交換,然後將新的無序區調整爲新的堆。

    因此對於堆排序,最重要的兩個操作就是構造初始堆和調整堆,其實構造初始堆事實上也是調整堆的過程,只不過構造初始堆是對所有的非葉節點都進行調整。

    下面舉例說明:

     給定一個整形數組a[]={16,7,3,20,17,8},對其進行堆排序。

    首先根據該數組元素構建一個完全二叉樹,得到

 
 然後需要構造初始堆,則從最後一個非葉節點開始調整,調整過程如下:

20和16交換後導致16不滿足堆的性質,因此需重新調整

這樣就得到了初始堆。

即每次調整都是從父節點、左孩子節點、右孩子節點三者中選擇最大者跟父節點進行交換(交換之後可能造成被交換的孩子節點不滿足堆的性質,因此每次交換之後要重新對被交換的孩子節點進行調整)。有了初始堆之後就可以進行排序了。

此時3位於堆頂不滿堆的性質,則需調整繼續調整

 這樣整個區間便已經有序了。
    從上述過程可知,堆排序其實也是一種選擇排序,是一種樹形選擇排序。只不過直接選擇排序中,爲了從R[1...n]中選擇最大記錄,需比較n-1次,然後從R[1...n-2]中選擇最大記錄需比較n-2次。事實上這n-2次比較中有很多已經在前面的n-1次比較中已經做過,而樹形選擇排序恰好利用樹形的特點保存了部分前面的比較結果,因此可以減少比較次數。對於n個關鍵字序列,最壞情況下每個節點需比較log2(n)次,因此其最壞情況下時間複雜度爲nlogn。堆排序爲不穩定排序,不適合記錄較少的排序。

實現代碼:
//array是待調整的堆數組,i是待調整的數組元素的位置,nlength是數組的長度
//本函數功能是:根據數組array構建大根堆
 
voidHeapAdjust(intarray[],inti,intnLength)
{
intnChild;
intnTemp;
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;
}
}
 
//堆排序算法
voidHeapSort(intarray[],intlength)
{
inttmp;
//調整序列的前半部分元素,調整完之後第一個元素是序列的最大的元素
//length/2-1是最後一個非葉節點,此處"/"爲整除
for(inti=length/2-1;i>=0;--i)
HeapAdjust(array,i,length);
//從最後一個元素開始對序列進行調整,不斷的縮小調整的範圍直到第一個元素
for(inti=length-1;i>0;--i)
{
//把第一個元素和當前的最後一個元素交換,
//保證當前的最後一個位置的元素都是在現在的這個序列之中最大的
///Swap(&array[0],&array[i]);
tmp=array[i];
array[i]=array[0];
array[0]=tmp;
//不斷縮小調整heap的範圍,每一次調整完畢保證第一個元素是當前序列的最大值
HeapAdjust(array,0,i);
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章