堆排序演算法

2021-07-16 07:44:18 字數 1342 閱讀 9300

9.7.2 堆排序演算法

堆排序(heap sort)就是利用堆(假設利用大頂堆)進行排序的方法。它的基本思想是,將待排序的序列構造成乙個大頂堆。此時,整個序列的最大值就是堆頂的根結點。將它移走(其實就是將其與堆陣列的末尾元素交換,此時末尾元素就是最大值),然後將剩餘的n-1個序列重新構造成乙個堆,這樣就會得到n個元素中的次小值。如此反覆執行,便能得到乙個有序序列了。

堆排序的時間複雜度為o(nlogn)

例如圖9-7-4,左圖是乙個大頂堆,90為最大值,將90與20(末尾元素)互換,如中圖所示,此時90就成了整個堆序列的最後乙個元素,將20經過調整,使得除90以外的結點繼續滿足大頂堆定義(所有結點都大於等於其子孩子),見右圖。然後再考慮將30與80互換……

相信大家有些明白堆排序的基本思想了,不過要實現它還需要解決兩個問題:(1)如何由乙個無序序列構建成乙個堆?(2)如果在輸出堆頂元素後,調整剩餘元素成為乙個新的堆?要解釋清楚它們,讓我們來看**。

複製**

/* 對順序表l進行堆排序 */

1 void heapsort(sqlist *l)

2 11 }

複製**

從**中也可以看出,整個排序過程分為兩個for迴圈。第乙個迴圈要完成的就是將現在的待排序序列構建成乙個大頂堆。第二個迴圈要完成的就是逐步將每個最大值的根結點與末尾元素交換,並且再調整其成為大頂堆。

假設我們要排序的序列是 ,那麼l.length=9,第乙個for迴圈,**第4行,i是從⌊9/2⌋=4開始,4→3→2→1的變數變化。為什麼不是從1到9,或者從9到1,而是從4到1呢?其實我們看了圖9-7-5就明白了,它們都有什麼規律?它們都是有孩子的結點。注意灰色結點的下標編號就是1、2、3、4。

我們所謂的將待排序的序列構建成為乙個大頂堆,其實就是從下往上,從右到左,將每個非終端結點(非葉結點)當作根結點,將其和其子樹調整成大頂堆。i的4→3→2→1的變數變化,其實也就是30,90,10、50的結點調整過程。

既然已經弄清楚i的變化是在調整哪些元素了,現在我們來看關鍵的heapadjust(堆調整)函式是如何實現的。

複製**

/* 已知l->r[s..m]中記錄的關鍵字除l->r[s]之外均滿足堆的定義, */

/* 本函式調整l->r[s]的關鍵字,使l->r[s..m]成為乙個大頂堆 */

1 void heapadjust(sqlist *l,int s,int m)

2 {

3 int temp,j;

4 temp=l->r[s];

5 for(j=2*s;j<=m;j*=2) /* 沿關鍵字較大的孩子結點向下篩選 */

6 {

7 if(j

排序演算法 堆排序

1 什麼是堆 首先它是一顆完全二叉樹,並且父結點的值大於子節點的值 最大堆 或父結點的值小於子結點的值 最小堆 小根堆 根結點 亦稱為堆頂 的關鍵字是堆裡所有結點關鍵字中最小者的堆稱為小根堆,又稱最小堆。大根堆 根結點 亦稱為堆頂 的關鍵字是堆裡所有結點關鍵字中最大者,稱為大根堆,又稱最大堆。2 堆...

排序演算法 堆排序

花了一晚上時間研究堆排序,這個排序困擾了哥很久,終於搞清楚了。一 堆的定義 1.父結點的鍵值總是大於或等於 小於或等於 任何乙個子節點的鍵值 2 每個結點的左子樹和右子樹都是乙個二叉堆 都是最大堆或最小堆 二 已知結點 i 則它的子結點 為2 i 1 與 2 i 2 父節點為 i 1 2 三 堆排序...

排序演算法 堆排序

由於不經常使用,之前學習看過的演算法都給忘了。現在把他們寫下來,記錄下來,以方便以後查閱。本篇文章的 即為堆排序的 主函式中是對輸入檔案中的序列進行排序,並將結果輸出到乙個檔案中。這是一種形式類似於google codejam的測試方法。include include using namespace...