1. 程式人生 > 實用技巧 >資料結構與演算法-堆排序

資料結構與演算法-堆排序

堆排序

  堆排序是指利用堆這種資料結構所設計的一種排序演算法。堆是一個近似完全二叉樹的結構,並同時滿足堆的性質:即子節點的鍵值或索引總是小於(或者大於)它的父節點,堆排序的時間複雜度為O(nlogn)。(來自維基百科)


什麼是堆

  堆是一種特殊的完全二叉樹,它的性質為:任意節點大於等於或者小於等於它的左右節點。如果任意節點大於等於它的左右節點,此堆稱為最大堆;反之,任意節點小於等於它的左右節點,此堆稱為最小堆。

堆節點的訪問

  • 父節點i的左子節點所在位置(2i+1)

  • 父節點i的右子節點所在位置(2i+2)

  • 子節點i的父節點所在位置(i-1)/2向下取整


堆排序的基本思路

  1. 無序的堆序列

  2. 將每個父節點跟左右子節點作比較,將大/小的子節點和父節點做交換,直到將堆變成最小堆或者最大堆

  3. 將堆頂的值跟未排序的二叉樹最後的一個節點做交換

  4. 重複步驟(2,3),直到堆頂

堆排序的步驟分解

無序的序列:{0,9,6,2,3,1,5},進行從大到小排序

上圖是構建一個初始化的最大堆,然後對堆頂和堆尾的數值作交換。

我們先對比(1)堆,從最後一個父節點開始,節點2的值都比子節點5,6都大,不需要做調整;節點1的值都比子節點3,4都大,也不需要作交換;節點0的值比左右子節點都小,然後取最大的一個子節點,(2)堆表示作交換,子節點做了交換,需要判斷它的子節點是否比自己大,節點4比節點1的大(因節點4比節點3要大,故取節點4做交換),(3)堆表示做交換。(4)堆完成了初始化的最大堆。(5)堆進行堆頂和堆尾的交換。

接下來的操作就是重複上述的流程,直到堆頂為止,看下圖的流程:

以上就是完整演算法的流程。


堆排序的實現程式碼

void myswap(int &a, int &b) {
    int tmp = a;
    a = b;
    b = tmp;
}

void MaxHead(int *pArrayNum, int nStart, int nEnd) {
    int nF = nStart;//當前父節點序號
    int nS = nF * 2 + 1;//當前父節點的左子節點序號
    while(nS <= nEnd) {
        //判斷左右子節點最大值
if((nS+1 <= nEnd) && (pArrayNum[nS+1] > pArrayNum[nS])) { nS++;//右節點大 } //判斷父節點與子節點的大小 if(pArrayNum[nS] > pArrayNum[nF]) { //子節點比父節點大,作交換 myswap(pArrayNum[nS], pArrayNum[nF]); //往下作對比 nF = nS; nS = nF * 2 + 1; } else { //父節點比子節點大,直接結束 return ; } } } void HeapSort(int *pArrayNum, int nCount) { //初始化,構建最大堆 for(int i=nCount/2-1; i>=0; i--) { MaxHead(pArrayNum, i, nCount-1); } //將堆頂的值與未排序的堆尾值做互換,然後再做堆構建 for (int i=nCount-1; i>0; i--) { //堆頂值與未排序的堆尾值互換 myswap(pArrayNum[0], pArrayNum[i]); MaxHead(pArrayNum, 0, i-1);//重新構建最大堆 } }