關於對的算法思想,隨處都可以查到,下面總結下堆的算法。
堆排序和直接選擇排序都是選擇排序,即從未序區間中選擇最大或者最小的元素追加到已序區間的尾部,直到剩下一個元素。
直接選擇排序中,為了從R[1..n]中選出關鍵字最小的記錄,必須進行n-1次比較,然后在R[2..n]中選出關鍵字最小的記錄,又需要做n-2次比較。事實上,后面的n-2次比較中,有許多比較可能在前面的n-1次比較中已經做過,但由於前一趟排序時未保留這些比較結果,所以后一趟排序時又重復執行了這些比較操作。
堆排序可通過樹形結構保存部分比較結果,可減少比較次數。
堆排序算法的解析:
1、建初始堆(以大頂堆為例),這一步驟把初始序列建成了一個滿足大頂堆性質的序列,且每棵子樹都滿足。這個時候堆頂是本序列中最大的元素,因此將最后一個元素和堆頂元素調換,把最大值放到最終的位置上。建好了初始堆,就保留了排序時候的比較結果,后面的調整都可以再此基礎上進行,加快排序效率。
2、由於每次講堆頂元素和最后一個對調,破壞了堆的性質,因此要從新向下調整,建立大頂堆(這里在初始堆的基礎上,只要將堆頂元素調到合適位置即可)。
3、調整完了之后,又將堆頂元素與未序區間的最后一個元素對調。
4、重復2,3直到堆中剩余一個元素。
下面是對算法的代碼:
1 void HeapSort(ElemType A[],int len) 2 { 3 BuilMaxHeap(A,len);//建立初始堆 4 for(int i = len;i>1;i--) 5 { 6 A[0]=A[1];A[1]=A[i];A[i]=A[0];//堆頂和未序區間尾元素對調。 7 AdjustDown(A,1,i-1);//調整未序區間,繼續下一次對調。從1 —— i-1是未序的。 8 } 9 }
建立大頂堆的算法如下:
1 void BuildMaxHeap(ElemType A[],int len) 2 { 3 for(int i=len/2;i>0;i--)//從最后一個父節點開始向前建堆 4 AdjustDown(A,i,len);//向下調整 5 }
向下調整算法如下:
1 void AdjustDown(Elemtype A[],int k,int len) 2 { 3 A[0]=A[k];//保存子堆的父節點 4 for(int i=2*k;i<=len;i*=2) 5 { 6 if(i<len&&A[i]<A[i+1])//尋找較大的孩子 7 i++; 8 if(A[0]>A[i])//如果孩子節點沒有比父節點大,跳出循環 9 break; 10 else 11 { 12 A[k]=A[i];//孩子上調 13 k=i;//孩子當下一個父親 14 } 15 } 16 A[k]=A[0];//把子堆頂元素放在最終的位置 17 }
注意:堆的最重要的操作就是調整函數,即向下調整AjustDown()月AdjustUp(),其余操作都建立在這兩個操作之上。
刪除堆頂元素時,需要堆頂元素和堆的最后一個元素對換,然后將堆的長度減一。
對堆查入操作時,先將堆的新節點放在堆的末尾,在對這個新節點指向向上調整AdjustUp()操作。
下面是向上調整的堆算法:
1 void AdjustUp(Elentype A[],int k)//k為向上調整的節點,也為新堆的元素個數 2 { 3 A[0]=A[k]; 4 int i = k/2;//i是k的雙親 5 while(i>0&&A[i]<A[0])//如果雙親存在,且雙親小於孩子節點,執行循環體中的語句 6 { 7 A[k]=A[i]; 8 k=i; 9 i=k/2;//尋找雙親的雙親,繼續向上比較 10 }//如果不滿足while中的條件跳出,k的位置就是接下來新節點的位置。 11 A[k]=A[0];//將新節點復制到最終位置上 12 }
堆的應用:
堆可以用來解決TOPK問題;優先級隊列的底層也是用堆來實現。