堆:大根堆與小根堆
堆排序是建立在堆基礎上的排序方法,首先了解一下什么是堆。
常用的堆一般有兩種,大根堆和小根堆。堆可以看做是一棵二叉樹,其父節點的值總是大於(大根堆)或者小於(小根堆)子節點的值。舉一個例子:
圖1 不滿足堆的條件 圖2大根堆 圖3 小根堆
圖1不是堆,因為不滿足父節點的值大於或者小於子節點的值;
圖2是大根堆,根節點是最大值,父節點都大於或等於子節點的值;
圖3是小根堆,根節點是最小值,父節點都小於或等於子節點的值。
堆排序
下面以大根堆為例講解堆排序:
大根堆有一個很好的性質,根節點的數值總是大於其他所有節點的數值,利用這個性質,可以實現排序的工作。堆排序的步驟可以描述如下:
1.構建大根堆。首先我們的原始數組一般情況下是不滿足堆的條件,既然我們要可用大根段的性質進行排序,第一步當然是對原始數組進行處理,構建大根堆。
2.根節點數據處理以及大根堆重構。構建了大根堆之后,根節點的數據是最大值,將該數值取出,對剩下的元素重構大根堆,這時根節點是剩下元素的最大值,取出。只要不斷重復上述的操作,不斷取出未排序元素的最大值,直到未排序的元素只剩一個,就完成了排序工作。
說得有點抽象,直接用一個實際的例子說明堆排序的工作步驟:
對一個無序的序列A={5,4,17,13,15,12,10 }按從小到大進行排序,序列的下標分別為{1,2,3,4,5,6,7},A[i]表示下標為i的元素。
第一步:對無序的數組構造大根堆
大根堆的根節點是整個序列的最大值。
第二步:
將A[1]與A[7]互換,此時A[7]為序列的最大值,A[7]已經排序完畢,剩余的元素A[1]~A[6]形成新的未排序序列,由於此時序列不是大根堆,需要重構大根堆。
第三步:
將A[1]與A[6]互換,此時A[6]為序列的最大值,A[6]已經排序完畢,剩余的元素A[1]~A[5]形成新的未排序序列,由於此時序列不是大根堆,需要重構大根堆。
第四步:
將A[1]與A[5]互換,此時A[5]為序列的最大值,A[5]已經排序完畢,剩余的元素A[1]~A[4]形成新的未排序序列,由於此時序列不是大根堆,需要重構大根堆。
第五步:
將A[1]與A[4]互換,此時A[4]為序列的最大值,A[4]已經排序完畢,剩余的元素A[1]~A[3]形成新的未排序序列,由於此時序列不是大根堆,需要重構大根堆。
第六步:
將A[1]與A[3]互換,此時A[3]為序列的最大值,A[3]已經排序完畢,由於此時未排序的序列只剩下兩個元素,而且A[0]>A[1],將A[0]與A[1]互換即可得到最終的已排序序列。
C++程序實現
從上述的排序過程可知,堆排序主要有兩個過程,大根堆的構建與重構:
1、大根堆的重構
如何實現大根堆的重構?如果一個原有的數組滿足大根堆的性質,而只有其中一個元素改變從而破壞了大根堆的性質,那么可以從該元素出發,不斷”逐級下降“,讓子節點小於等於父子節點,滿足大根堆的性質。以一個實例說明重構的過程:
堆排序的過程中,每一次改變的只有根節點的元素,因此只需要從根節點出發,進行如上圖的操作即可實現大根堆的重構。C++代碼如下:
1 void ReBuildMaxHeap(int *array,int arraylength,int startIndex){ 2 int index=0; 3 //int j=0; 4 int max=0; 5 while((2*startIndex+1)<arraylength){ 6 index = startIndex; 7 max = array[index]; 8 if(max<(array[2*startIndex+1])){ 9 max=array[2*startIndex+1]; 10 index=2*startIndex+1; 11 } 12 if(2*startIndex+2<arraylength&&max<array[2*startIndex+2]){ 13 max=array[2*startIndex+2]; 14 index=2*startIndex+2; 15 } 16 if(index==startIndex){ 17 break; 18 } 19 swap(array,index,startIndex); 20 startIndex = index; 21 } 22 }
1、 構建大根堆。
要構建大根堆,只需要遍歷所有非葉子節點元素,使其所有的葉子節點均不大於足該節點元素即可。構建大根堆自低向上對數組進行遍歷,如果發現父節點的值小於子節點的值,則將父節點的值與子節點的最大值進行交換,保證父節點的數據總是大於子節點的數據。如果發生了數據的交換,有可能令子節點不滿足大根堆條件,需要進行會輸重構大根堆。以一個實際的例子說明構建大根堆的過程:
C++代碼如下:
1 void createHeap(int *array,int length){ 2 int max=0; 3 int index=0; 4 for(int i=floor(length/2)-1;i>=0;i--){ 5 index = i; 6 max = array[i]; 7 if(max<array[2*i+1]){ 8 index=2*i+1; 9 max = array[2*i+1]; 10 } 11 if(2*i+2<length&&max<array[2*i+2]){ 12 index = 2*i+2; 13 max = array[2*i+2]; 14 } 15 if(index!=i){ 16 swap(array,index,i); 17 ReBuildMaxValue(array,length,index); 18 } 19 } 20 }
全部源代碼:
1 #include "stdafx.h" 2 #include <iostream> 3 using namespace std; 4 5 //兩個文位置的數據交換 6 void swap(int *array,int num1,int num2){ 7 int temp = *(array+num1); 8 *(array+num1) = *(array+num2); 9 *(array+num2) = temp; 10 } 11 12 //重構大根堆 13 void ReBuildMaxValue(int *array,int arraylength,int startIndex){ 14 int index=0; 15 //int j=0; 16 int max=0; 17 while((2*startIndex+1)<arraylength){ 18 index = startIndex; 19 max = array[index]; 20 if(max<(array[2*startIndex+1])){ 21 max=array[2*startIndex+1]; 22 index=2*startIndex+1; 23 } 24 if(2*startIndex+2<arraylength&&max<array[2*startIndex+2]){ 25 max=array[2*startIndex+2]; 26 index=2*startIndex+2; 27 } 28 if(index==startIndex){ 29 break; 30 } 31 swap(array,index,startIndex); 32 startIndex = index; 33 } 34 } 35 36 //構建大根堆 37 void createHeap(int *array,int length){ 38 int max=0; 39 int index=0; 40 for(int i=floor(length/2)-1;i>=0;i--){ 41 index = i; 42 max = array[i]; 43 if(max<array[2*i+1]){ 44 index=2*i+1; 45 max = array[2*i+1]; 46 } 47 if(2*i+2<length&&max<array[2*i+2]){ 48 index = 2*i+2; 49 max = array[2*i+2]; 50 } 51 if(index!=i){ 52 swap(array,index,i); 53 ReBuildMaxValue(array,length,index); 54 } 55 } 56 } 57 58 //堆排序 59 void heapSort(int *array,int arraylength){ 60 createHeap(array,10); 61 for(int i=arraylength-1;i>0;i--){ 62 swap(array,i,0); 63 ReBuildMaxValue(array,i,0); 64 } 65 } 66 67 int main(int argc, _TCHAR* argv[]) 68 { 69 int values[10]={5,4,17,13,15,12,10,7,11,9}; 70 heapSort(values,10); 71 for(int i=0;i<10;i++){ 72 cout<<*(values+i)<<endl; 73 } 74 return 0; 75 }