排序演算法 堆排序

2021-09-12 19:01:19 字數 3171 閱讀 3973

堆排序是指利用堆這種資料結構所設計的一種排序演算法。

型別:選擇排序

時間複雜度(最壞):o(nlogn)

時間複雜度(最好):o(nlogn)

時間複雜度(平均):o(nlogn)

空間複雜度:o(1)

穩定性:不穩定(堆的根元素與最後乙個葉節點交換值時會導致不穩定)

堆是乙個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。且完全二叉樹可以基於陣列儲存(父子節點的關係可以用陣列下標表示),加持上堆的特性,故可以做堆排序。

滿二叉樹:除最後一層無任何子節點外,每一層上的所有結點都有兩個子結點二叉樹;即每一層節點數都達到最大值;即深度為 k 的二叉樹節點個數為 2^k-1,則為滿二叉樹。

完全二叉樹:若設二叉樹的深度為 k,除第 k 層外,其它各層 (1~k-1) 的結點數都達到最大個數,且第 k 層所有的結點都連續集中在最左邊,這就是完全二叉樹。

堆:基於完全二叉樹,分為大頂堆/小頂堆。各節點的數值皆大於其左右子節點的數值(大頂堆),或各節點的數值皆小於其左右子節點的數值(小頂堆)。

大頂堆,節點的數值皆大於子節點的數值,下文都以大頂堆為例項講解

因為是基於完全二叉樹的,所以堆還有一些相應的特性:

1、位序為 n 的節點,其父節點位序為 (int) n/2

2、位序為 n 的節點,其左子節點位序為 2n,右子節點位序為 2n+1(這是按照位序從1開始計算,但對程式語言不太友好,如果位序從0開始計算,則左子節點位序為 2n+1, 右子節點位序為 2n+2)

按照陣列下標的方式去編號的話如下:

0

/ \

1 2

/ \ / \

3 4 5 6

.../

floor(n/2) ...

/ n

/ \2n+1 2n+2

可以發現,所有非葉子節點的序號都會落在0~(int) n/2-1區間中,n為節點總數,例如:

1、有4個節點,節點編號為0~3,非葉子節點編號區間值為 0~1,即編號為 0,1 的節點為非葉子節點。

2、有5個節點,節點編號為0~4,非葉子節點編號區間值為 0~1,即編號為 0,1 的節點為非葉子節點。

3、有6個節點,節點編號為0~5,非葉子節點編號區間值為 0~2,即編號為 0,1,2 的節點為非葉子節點。

4、有7個節點,節點編號為0~6,非葉子節點編號區間值為 0~2,即編號為 0,1,2 的節點為非葉子節點。

可以很方便的獲取完全二叉樹的非葉子節點的序號集合,從 k-1 層開始,依次將各非葉子節點及其左右子節點視為乙個完全二叉樹,將其調整至大頂堆,直至根節點,這時整個二叉樹就是乙個大頂堆

我們有了非葉子節點的序號及獲取左右節點序號的算式,所以很容易就可以將各非葉子節點及其左右子節點組成的完全二叉樹調整至大頂堆。同時要注意如果非葉子節點同其左右子節點發生了調整,其左右子節點如果也是非葉子節點的話,也要檢測是否破壞了堆特性,如破壞也需進行調整。

堆排序:

即堆排序的過程:將待排序陣列對映到完全二叉樹中,通過調整完全二叉樹至大頂堆,獲取根節點的值(節點最大值)

那大頂堆的構建如何做呢?我用比較白話的方式講一下

1、從 k 層開始,將每一層子節點的最大值與其父節點的值進行比較調整(如發生交換且子節點為非葉子節點,也要檢查子節點的樹是否滿足了父節點大於子節點的特性)

2、重複第一步驟直到根節點,此時根節點的值即為完全二叉樹節點中的最大值,即生成了大頂堆

時間複雜度:o(nlogn)

空間複雜度:o(1)

golang 原始碼例項

package main

import (

"fmt"

)func main()

heapsort(arr)

fmt.print("sorted: ", arr)

}// 堆排序

func heapsort(arr int)

// 列印下標 0 ~ arrlengthunsorted-1 的數列

fmt.println("current heap: ", arr[0:arrlengthunsorted])

// 一次大頂堆構建完成,根節點為堆最大值,與無序區堆的最後乙個節點交換

// 無序區節點數-1

// 破壞了堆結構 開始對新無序區做大頂堆構建

swapitemofarray(arr, 0, arrlengthunsorted-1)

}}// 將子樹調整為大頂堆

func heapbuild(arr int, nodeindex int, arrlengthunsorted int)

if rightchildnodeindex < arrlengthunsorted && arr[rightchildnodeindex] > arr[nodeindex]

}// 交換陣列兩個元素

func swapitemofarray(arr int, indexx int, indexy int)

執行過程及結果

current heap: [9 8 7 6 5 2 3 1 4]

current heap: [8 6 7 4 5 2 3 1]

current heap: [7 5 6 1 4 2 3]

current heap: [6 4 5 1 3 2]

current heap: [5 3 4 1 2]

current heap: [4 2 3 1]

current heap: [3 1 2]

current heap: [2 1]

current heap: [1]

sorted: [1 2 3 4 5 6 7 8 9]

排序演算法 堆排序

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

排序演算法 堆排序

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

排序演算法 堆排序

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