注:以下堆排序的**借鑑了部落格
這個排序演算法的時間複雜度為o(nlogn),主要是花費在建堆和調整堆上。所以對記錄數較少的檔案並不值得提倡,但是由於它的最壞時間複雜度是o(nlogn),所以對於記錄數較多的檔案是比較有效的。
完全二叉樹如果滿足每乙個父節點上的元素都大於左右孩子的叫做大根堆,其根節點是最大的。每乙個父節點上的元素都小於左右孩子的叫做小根堆,其根節點是最小的。
我們一般用一維陣列來儲存堆元素。
如果從下標0開始儲存
i節點上的父節點下標為(i-1)/2(取整)左右孩子節點下標為2i+1和2i+2.
1.建立乙個大根堆(或者小根堆)
2.每次取堆頂元素為最大的(或者最小的)排序
3.調整堆,使其維持大根堆(或小根堆)的性質
4.重複2
從步驟上來說,堆排序是一種選擇排序。每次選取最小的或者最大的放在有序區,然後對無序區繼續選擇,直到無序區空為止。
具體來講,我們先要把乙個無序序列建成堆,建堆的過程是乙個從下到上調整堆的過程,即從最後乙個父節點開始調整,然後向上擴充套件,直至整個變成堆。
然後再輸出堆頂元素(最大值或者最小值),即刪除堆頂元素。
然後再調整剩下的元素成為堆,即插入元素。重複以上步驟直到所有元素有序。
將元素插入到最後乙個節點後面,然後依次向上比較它的父節點。
**如下:
void minheapfixup(int a, int i)
//在最小堆中加入新的資料nnum
void minheapaddnumber(int a, int n, int nnum)
每次只能刪除第0個元素,為了便於重建堆,我們交換最後乙個元素和第0個元素的值,然後自上而下的調整。每一次都是在左右孩子中取最小的或者最大的孩子與父節點進行交換。
// 從i節點開始調整,n為節點總數 從0開始計算 i節點的子節點為 2*i+1, 2*i+2
void minheapfixdown(int a, int i, int n)
a[i] = temp;
}//在最小堆中刪除數
void minheapdeletenumber(int a, int n)
//建立最小堆
void makeminheap(int a, int n)
整個過程是從最後乙個父節點開始比較,即先讓子堆成為最小堆,然後再讓父堆成為最小堆。
注意第2步的排序細節,如果是只利用原來的陣列排序,那麼由於每次交換都是跟最後乙個未排序的數進行交換,想要得到公升序排列,就要用大根堆,降序排列用小根堆。如果你只是想輸出的話,順序相反。
下面證明一下堆排序的時間複雜度和空間複雜度:
堆排序的時間主要花費在建堆和調整堆上面。
假設總長度n,樹高k,則倒數第2層的元素有2^(k-1)個,最多需要進行1次下沉,最多需要的交換次數為1*2^(k-1),倒數第三層的元素最多需要2次下沉,最多需要的交換次數為2*2^(k-2).依次類推,建堆的時間為
t(k) = 1*2^(k-1)+2*2^(k-2)+3*2^(k-3)+……+(k-1)*2+k*2^0
乘以2再將兩式相減得
t(k) = 2^(k+1)-2-k,代入k = logn,t(k) = 2n -2-logn
所以時間複雜度為o(n)
當我們插入乙個元素再調整堆的時候,因為只需要調整堆頂的位置,最多下沉到底部,需要的時間是logn級別的,然後一共需要插入n個,所以是nlogn級別的。
其它操作是常數級別的。
所以堆排序的時間複雜度是o(nlogn)。
因為是原地排序,只需要乙個記錄大小的輔助空間,所以空間複雜度o(1).
所以當問到需要空間複雜度為常數級別的題目時,要想到堆排序。
堆排序學習
資料來自 靜默虛空 萬分感謝 做筆記用 1 根據初始陣列去構造初始堆 構建乙個完全二叉樹,保證所有的父結點都比它的孩子結點數值大 2 每次交換第乙個和最後乙個元素,輸出最後乙個元素 最大值 然後把剩下元素重新調整為大根堆。設有乙個無序序列 構建初始堆 完整的堆排序處理 如果父結點的值已經大於孩子結點...
堆排序學習
參考博主dreamcatcher cx 這位博主還有許多優秀的排序方法學習記錄 構建n次最大堆,每次構建頂點元素即為陣列最大元素,找到最大元素之後將最大元素放在陣列最後 從最後乙個非葉子節點開始,將該節點調整最低階節點,也即它的小於父節點大於兩個子節點 調整次數為n 2 1 完成最大堆的構建之後,將...
堆排序學習筆記
參考此文的學習筆記 不用交換,則已經是最大堆,不用迴圈了 else break void sort int a,int length 開始排序,排序是從上到下從左到右的調整 for int j length 1 j 0 j int main sort a,length for int i 0 i l...