排序演算法 之堆排序

2021-12-29 22:37:42 字數 2621 閱讀 3392

特別說明,這裡的序列都是從1開始。

對於n個元素的序列當且僅當滿足下列關係之一時,稱之為堆:

(1) ri <= r2i+1 且 ri <= r2i+2 (小根堆)

(2) ri >= r2i+1 且 ri >= r2i+2 (大根堆)

堆排序是一種樹形選擇排序,它的特點是在排序的過程中,將r[1..n]看成是一棵完全二叉樹的順序儲存結構,利用完全二叉樹中雙親結點和孩子結點之間的內在聯絡,在當前無序區中選擇關鍵字最大或者最小的記錄。

堆實際上是一棵以順序方式儲存的完全二叉樹,並滿足如下性質:樹中任一非葉子結點的關鍵字均不大於或者不小於其左右孩子的關鍵字。例如關鍵字序列(12, 36, 24, 85, 47, 30, 53, 91)和(96, 83, 27, 28, 11, 09)分別是滿足上述性質的小根堆和大根堆。第乙個序列對應的二叉樹如下圖:

顯然,堆中任一子樹也是堆。

堆排序利用了大根堆(或小根堆)中堆頂記錄的關鍵字最大(或最小),這一特性,使得在當前無序區中選取最大(最小)的記錄變得簡單。下面以大根堆為例說明堆排序的基本過程。

大根堆排序演算法的基本操作,初始化操作是按堆的定義將陣列r[1..n]調整為初始堆。每一趟排序的基本操作是,將當前無序區的堆頂記錄r[1]和該區間的最後乙個記錄交換,然後將新的無序區調整為堆(也叫作重建堆)。顯然,只需做n-1趟排序,選出較大的n-1個關鍵字,即可使得序列遞增有序。

以上思想可歸納為兩個操作:

(1)根據初始陣列去構造初始堆(構建乙個完全二叉樹,保證所有的父結點都比它的孩子結點數值大)。

(2)每次交換第乙個和最後乙個元素,輸出最後乙個元素(最大值),然後把剩下元素重新調整為大根堆。

當輸出完最後乙個元素後,這個陣列已經是按照從小到大的順序排列了。

因為構造初始堆也必須使用到堆的調整,所以我們先討論堆的調整演算法的實現。

從前面的討論中已經知道,在每趟排序開始前,r[1..i]是以r[1]為根的堆,當r[1]與r[i]交換後,新的無序區r[1..i-1]中只有r[1]的值發生了變化,故只有r[1]可能違反堆性質。因此,當被調整區間是r[low..high]時,除根結點r[low]可能違反堆性質外,r[low]的左右子樹(若存在)均已是堆。

調整r[low..high]的具體方法是:從r[low]和它的兩個孩子結點r[2*low]和r[2*low+1]開始。r[low]不小於這兩個孩子結點,則r[low]未違反堆的性質,以r[low]為根的樹已是堆。否則,必須將r[low]和它的兩個孩子中最大者進行交換。令r[large]表示兩個孩子之間的較大者,則r[low]應與r[large]進行交換。交換後又可能使結點r[large]違反堆性質,可以重複上述調整過程,對以r[large]為根的樹繼續進行調整。此過程直至當前被調整的結點已滿足堆性質,或者該結點已經是葉子結點為止。

上述方法就像過篩子一樣,把較小的關鍵字逐層篩下去,而將較大的關鍵字逐層選上來,因此也將此方法稱為「篩選法」。下面是具體的演算法過程:

template

void sift(int low, int high)

r[low] = temp;

}要將r[1..n]調整為乙個大根堆,就必須將它所對應的完全二叉樹中以每一結點為根的子樹都調整為堆。顯然,空樹可以看成是堆,只有乙個結點的樹也是堆,而在完全二叉樹中,所有序號i > n/2 的結點都是葉子,因此,以這些結點為根的子樹均已是堆。因此,我們只需將序號i從n/2~1反覆呼叫sift()演算法,便可完成建立初始堆的工作。下面是具體演算法:

下面是堆排序的**實現

void heapsort()

//for (int i = 1; i <= n; ++i)

cout << r[i] << ' ';

cout << endl;

}由於建立初始堆所需的比較次數較多,所以堆排序一般不適宜用於記錄數較少的序列或檔案。

已知序列,寫下採用堆排序法對該序列作降序排序時每一趟的結果。

排序演算法之堆排序

前言 今天我來介紹下堆排序,在寫堆排序 之前,我們要知道堆的概念!堆的定義 n個關鍵字序列kl,k2,kn稱為 heap 當且僅當該序列滿足如下性質 簡稱為堆性質 1 ki k 2i 且ki k 2i 1 1 i n 當然,這是小根堆,大根堆則換成 號。k i 相當於二叉樹的非葉子結點,k 2i 則...

排序演算法之堆排序

堆排序演算法是選擇排序的一種,該演算法只是通過堆,最大堆 或者最小堆選擇出乙個待排序序列中的最大值,或者最小值。要想實現堆排序演算法,就需要構建什麼堆,這裡也最小堆為例。說明什麼是堆,怎麼構建乙個堆。假設待排序序列為a n 為乙個陣列。陣列的長度為n 陣列下標為 0,1,2,i,2i,2i 1 n ...

排序演算法之堆排序

宣告 本博文 為樓主親自編寫並測試,其它內容引用至我一直很崇拜的牛人morewindows。他對排序演算法的講解通俗易懂,給人一種耳目一新的感覺。堆排序與快速排序 歸併排序 一樣都是時間複雜度為o n logn 的幾種常見排序方法。最小堆的講解以及最小堆元素的插入和刪除參見最小堆操作。以下繼續引用以...