一、堆排序概述
當你看到這里請去原作者那里點個贊, 原文鏈接
1.堆是一種數據結構
可以將堆看作一棵完全二叉樹,這棵二叉樹滿足,任何一個非葉節點的值都不大於(或不小於)其左右孩子節點的值。
2. 堆的存儲
一般用數組來表示堆,若根節點存在於序號0處,i結點的父結點下表就為(i-1)/2,i結點的左右子結點下標分別為2i+1和2i+2
3. 堆排序思想
利用大頂堆(小頂堆)堆頂記錄的是最大關鍵字(最小關鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。
4. 堆排序的實現
堆排序需要解決兩個問題:
- 如何由一個無序序列建成一個堆
- 如何在輸出堆頂元素之后,調整剩余元素成為一個新的堆?
針對第二個問題:一般在輸出堆頂元素之后,視為將這個元素排除,然后用表中最后一個元素填補它的位置,自上向下進行調整:首先將堆頂元素和它的左右子樹的根結點進行比較,把最小的元素交換到堆頂;然后順着被破壞的路徑一路調整下去,直至葉子結點,就得到新的堆。
Step 1: 構造初始堆
初始化堆時是對所有的非葉子結點進行篩選
最后一個非終端元素的下標是[n/2]向下取整,所以篩選只需要從第[n/2]向下取整個元素開始,從后往前進行調整。
Step 2:進行堆排序
堆排序是一種選擇排序。建立的初始堆為初始的無序區。
排序開始,首先輸出堆頂元素(因為它是最值),將堆頂元素和最后一個元素交換,這樣,第n個位置(即最后一個位置)作為有序區,前n-1個位置仍是無序區,對無序區進行調整,得到堆之后,再交換堆頂和最后一個元素,這樣有序區長度變為2。。。
不斷進行此操作,將剩下的元素重新調整為堆,然后輸出堆頂元素到有序區。每次交換都導致無序區-1,有序區+1。不斷重復此過程直到有序區長度增長為n-1,排序完成。
5. 堆排序實例
1)首先,建立初始的堆結構圖
2)然后,交換堆頂的元素和最后一個元素,此時最后一個位置作為有序區(有序區顯示為黃色),然后進行其他無序區的堆調整,重新得到大頂堆后,交換堆頂和倒數第二個元素的位置……
3)重復此過程
4)最后,有序區擴展完成即排序完成
由排序過程可見,若想得到升序,則建立大頂堆,若想得到降序,則建立小頂堆。
6. 堆排序分析
穩定性:
時間復雜度:O(nlogn)
空間復雜度:
堆排序方法對記錄數較少的文件並不值得提倡,但對n較大的文件還是很有效的。因為其運行時間主要耗費在建初始堆和調整建新堆時進行的反復“篩選”上。
二: Python代碼
簡潔版:

def sift_down(arr, start, end): root = start while True: # 從root開始對最大堆調整 child = 2 * root + 1 if child > end: break # 找出兩個child中交大的一個 if child + 1 <= end and arr[child] < arr[child + 1]: child += 1 if arr[root] < arr[child]: # 最大堆小於較大的child, 交換順序 arr[root], arr[child] = arr[child], arr[root] # 正在調整的節點設置為root root = child else: # 無需調整的時候, 退出 break def heap_sort(arr): # 從最后一個有子節點的孩子來調整最大堆 first = len(arr) // 2 - 1 for start in range(first, -1, -1): sift_down(arr, start, len(arr) - 1) # 將最大的放到堆的最后一個, 堆-1, 繼續調整排序 for end in range(len(arr) -1, 0, -1): arr[0], arr[end] = arr[end], arr[0] sift_down(arr, 0, end - 1)
注釋版:

def sift_down(arr, start, end): root = start while True: # 從root開始對最大堆調整 child = 2 * root + 1 # child為root的左孩子 if child > end: # 超出序列的范圍 break # 找出兩個child中較大的一個 if child + 1 <= end and arr[child] < arr[child + 1]: # 如果右孩子存在, 而且左孩子小於右孩子 child += 1 # 將child指向右孩子 if arr[root] < arr[child]: # 如果root 小於 它的較大child arr[root], arr[child] = arr[child], arr[root] # root 與 child, 交換位置 root = child # 正在調整的節點設置為root else: # 無需調整的時候, 退出 break def heap_sort(arr): # 從最后一個有子節點的節點 來 調整最大堆 first = len(arr) // 2 - 1 # first: 最后一個有子節點的節點的下標 for start in range(first, -1, -1): sift_down(arr, start, len(arr) - 1) # 將堆內的首節點存儲到有序區 for end in range(len(arr) -1, 0, -1): arr[0], arr[end] = arr[end], arr[0] sift_down(arr, 0, end - 1) """ 有序區: 從尾節點的位置, 根據循環的次數, 依次向前移動, 直到堆為空, 即: 有序區的長度為堆的原始長度 1. for end in range(len(arr)-1, 0, -1) len(arr)-1: arr內的最后一個節點的下標 end: 從arr的最后一個節點開始遍歷, 即: 將arr倒序遍歷 2. arr[0], arr[end] = arr[end], arr[0] 將arr內的首節點, 存儲到有序區 1. arr[0]: root節點, 堆頂, arr內的第一個元素, 如果arr是大頂堆, 則是arr內的最大元素, 如果arr是小頂堆, 則是arr內的最小元素 2. arr[end]: arr內的尾節點, 有序區的首節點 根據循環的次數, 位置依次向前移動 """ if __name__ == "__main__": l = list(i for i in range(0, 1000)) print("洗牌之前的列表:" + str(l)) random.shuffle(l) print("洗牌之后的列表:" + str(l)) heap_sort(l) print(l)