排序——堆排序法

1、堆排序法的概念算法

堆排序(Heap Sort)也是一種選擇排序算法,堆排序是利用堆的特性進行排序的過程。數組

2、算法描述測試

    堆是一個徹底二叉樹,樹中每一個節點對應於原始數據的一個記錄,而且每一個節點應知足如下條件:非葉節點的數據大於或等於其左、右孩子節點的數據(如果按從大到小的順序排序,則要求非葉節點的數據小於或等於其左、右孩子節點的數據)。
提示:在堆中,對節點的左孩子和右孩子的大小沒有要求,只規定父節點和子節點數據之間必須知足的條件。
    當要按從小到大的順序輸出數據時,要求根節點爲最大值。
    下面以按從小到大的順序排序爲例,介紹堆排序的相關內容。
由堆的定義可看出,其根節點爲最大值,堆排序就是利用這一特色進行排序的。堆排序過程包括如下兩個階段:
(1)將無序的數據構成堆(即用無序數據生成知足堆定義的徹底二叉樹)。
(2)利用堆排序(即將上一步生成的堆輸出,獲得排序後的有序數據)。code


1.構成堆
構成堆就是把無序的數據按堆的定義進行交換調整,使父節點的數據大於子節點的數據。構成堆的具體步驟以下。
(1)將無序數據放入徹底二叉樹的各節點。
(2)由二叉樹的下層向上層逐層進行父子節點的數據比較,使用一種稱爲「篩」的運算進行節點數據的調整,直到使節點最後知足堆的條件爲止。
    篩運算針對非葉節點進行,以非葉節點A i 的篩過程爲例,當對A進行篩運算時,比它編號i大的非葉節點都已進行過篩運算,即已造成了以各非葉節點爲根的堆,其中包括以A i 的左、右孩子爲根的堆(若A 2i 和A 2i+1 爲葉節點,則認爲葉節點是堆)。因此,對A i 進行篩運算是在其左、右子樹均爲堆的基礎上實現的。具體過程爲:
(1)肯定A i 的兩個子樹的最大值,放在A j 中。
(2)將A i 的數據與A的數據進行比較,若是A爲根的子樹已構成堆,篩運j i ≥A j ,表示以A i 算完成。
(3)若A i <A j ,則將A i 與A j 互換位置,互換位置後可能會破壞以A i (此時A i 的值爲原來的A j )爲根的堆,接着再以A j 爲根重複前面的步驟,直到父節點數據大於子節點,或子節點爲空時爲止。這樣,以A j 爲根的子樹就被調整爲一個堆。
提示:在以上過程當中,在對A j 進行的篩運算中,若其值較小,則會被逐層下移。這樣,較小的數據就像被篩子篩下去,而較大的數據保留在篩子上面,因此把構成堆的過程形象地稱爲篩運算。
    下面以一組待排序的數據演示構成堆的過程,假設有8個須要排序的數據序列以下:
69,65,90,37,92,6,28,54排序

下圖1,給出構成堆的全過程,a圖爲無序數據構成的徹底二叉樹,具體過程以下:io

 

 

  圖1構成堆過程class

(1)首先對最後一個非葉節點(節點4)進行篩運算,使其與子節點進行比較,因該子節點只有左子樹(節點8),而37<54,需對這兩個節點位置進行互換,獲得b圖。此時,節點4及其子節點構成堆。
(2)接着對二叉樹中倒數第2個非葉節點(節點3)進行篩運算,因節點3的兩個子節點中節點7的值較大,所以使節點3與節點7進行比較,90>28,不須要進行互換,獲得圖c。此時,節點3及其子節點構成堆。
(3)對二叉樹中倒數第3個非葉節點(節點2)進行篩運算,因節點2的兩個子節點中節點5的值較大,所以使節點2與節點5進行比較,65<92,將節點2與節點5的數據進行互換,獲得圖d。此時,節點2及其子節點構成堆。
(4)對二叉樹中倒數第4個非葉節點(節點1)進行篩運算,因節點1的兩個子節點中節點2的值較大,所以使節點1與節點2進行比較,69<92,將節點1與節點2的數據進行互換,獲得圖e。此時,因節點2的值已改變,可能破壞了節點2與其子節點構成的堆,須要從新對節點2及其子節點進行運算。在本例中,節點2比兩個子節點的值都大,符合堆的要求。此時,節點1及其子節點構成堆。至此,就將徹底二叉樹構成了一個知足要求的堆。基礎

2.利用堆排序
    使用上面的步驟構成堆之後,接下來的工做就是經過堆輸出有序的數據。
圖1_e生成的堆可看出,節點1的值是整個二叉樹中最大的,按順序輸出時,需將其放在數組的最後。根據這個規律,將堆按順序輸出數據的過程以下:
(1)取堆的根節點(最大值),將其放到數組的最後。因爲數組元素的最後一個元素保存着節點8的值37,所以,將節點8的值與節點1互換。
(2)節點8的值37換到根節點後,從新執行前面介紹的構成堆的方法,此時,應將最後一個節點排除在外。
(3)重複上面的過程,逐步從根節點取出最大值,放入數組的後面,再對剩下的節點從新構形成堆,直到只剩下一個節點,便可獲得有序的數據。
根據以上描述過程,將圖1_e所示的堆進行排序,如下圖2所示。具體過程以下:
(1)從a圖的根節點選擇,將其與最後一個節點8交換,獲得b圖
(2)根據b圖從新構成堆,獲得c圖
(3)重複上面的步驟,將根節點與最後一個節點(節點7)交換,獲得d圖,再將剩下的節點從新構成堆。就這樣不斷重複,最後可獲得排序後的數據。二叉樹

圖2堆排序gc

3、算法實現

一、堆排序法

/**
 * 構成堆
 * */
void HeapAdjust(int a[], int s, int n)
{
    int j, t;

    while (2*s+1 < n) { //第s個節點有子樹
        j = s*2 +1;
        if ((j+1) <n) {
        	if (a[j] < a[j+1]) //若是左子樹小於右子樹,則須要比較右子樹
        		j++;
        }

        if (a[s] < a[j]) { //比較s節點和子樹的數據大小,若是小於子樹,則交換
        	t = a[s];
        	a[s] = a[j];
        	a[j] = t;
        	s = j; //堆被破壞,從新調整
        } else {
        	break;
        }
    }
}

/**
 * 堆排序
 * */
void HeapSort(int a[], int n)
{
	int t, i;

	for (i=n/2-1; i>=0; i--) //將a[0]至a[n-1]構成堆
		HeapAdjust(a, i, n);

	for (i=n-1; i>0; i--) { //取根節點與末節點進行交換
		t = a[0];	//與第i個記錄進行交換
		a[0] = a[i];
		a[i] = t;
		HeapAdjust(a, 0, i); //將a[0]至a[i]從新調整爲堆
	}
}

二、堆排序法測試

#include <stdio.h>
#include <stdlib.h>
#include "HeapSort.c"

#define ARRAYLEN 8

void ShowData(int arr[], int n)
{
    int i;
    for (i=0; i<n; i++)
        printf("%d ", arr[i]);
    printf("\n");

    return;
}

int main(int argc, char *argv[])
{
    int i;
    int a[ARRAYLEN] = {69, 65, 90, 37, 92, 6, 28, 54};

    printf("原數據:");
    ShowData(a, ARRAYLEN);

    HeapSort(a, ARRAYLEN);
    printf("排序後:");
    ShowData(a, ARRAYLEN);
    return 0;

}

運行結果:

相關文章
相關標籤/搜索