排序演算法之堆排序(關鍵詞:資料結構/演算法/排序演算法/堆排序)
阿新 • • 發佈:2018-12-09
假定:有 1 個亂序的數列 nums ,其中有 n 個數。
要求:排好序之後是 從小到大 的順序。
堆排序演算法
原理
-
先將原始的堆,調整為最大堆:
從倒數第 1 個有子結點的結點(下標為 index = n//2 - 1)開始,將以結點 index 為根結點的子堆調整為最大堆;
index 範圍是 n//2 - 1(含) 到 0(含);
最後,原始的堆調整為最大堆。 -
將最大堆調整為(從小到大排列)有序的最小堆:
a. 將最大堆的堆頂第 0 項與第 n-1 項交換位置,這樣,第 1 大值排在堆的尾部;
b. 這樣,除開第 n-1 項的剩餘項組成的子堆,不是最大堆也不是最小堆,將這個子堆調整為最大堆;
c. 重複上述過程 a、b;
最終得到 1 個從小到大排列的最小堆。
關鍵程式碼的我的理解:
filter_down 函式:
特別要說明其中的 if-else。如果 根結點 的值 rootVal 較大,則不將其往下過濾(break);如果 rootVal 比子結點小,則將較大值往上移動,為 rootVal 騰出空間,最終將 rootVal 下濾。
值得注意的是,filter_down 函式本身,只是將堆中的最大項調整到頂部,較小項調整到尾部,並不會將 子堆 完全 調整為 最大堆,(結合測試用例中的 nums6、nums7 ,畫圖輔助理解。)
只有在 heap_sort 中,從後往前呼叫 filter_down 函式(第 1 個 for 迴圈),這樣的方式,才能得到完整的最大堆。
程式碼
# coding:utf-8 from swap import swap """ 將二叉樹中的,以 nums[p] 為根的子堆, 調整為最大堆。 (下濾 根節點 nums[p]) p 是根節點所在的下標; n 是當前堆一共有多少個元素。 """ def filter_down(nums, p, n): ''' 取出根節點存放的值 ''' rootVal = nums[p] parentIdx = p while 2*parentIdx+1 <= n-1: ''' kidIdx 暫時指向左兒子 ''' kidIdx = 2*parentIdx+1 ''' 左兒子 kidIdx 不等於 n-1,即左兒子不是最後 1 個結點, 也就是說,還有右兒子 ''' if kidIdx != n-1 and nums[kidIdx] < nums[kidIdx+1]: kidIdx += 1 ''' kidIdx 指向較大的子結點 ''' ''' rootVal 找到了合適的位置 ''' if rootVal >= nums[kidIdx]: break ''' 如果 rootVal 比子結點小, 則下濾 rootVal。 (將較大值往上移動)''' else: nums[parentIdx] = nums[kidIdx] ''' 將父節點的指標往下移動 ''' parentIdx = kidIdx nums[parentIdx] = rootVal def heap_sort(nums): n = len(nums) ''' 建立最大堆 ''' for index in range(n//2-1, -1, -1): filter_down(nums, index, n) ''' 刪除最大堆的頂部的最大項 將最大堆的最大項與最後 1 項交換位置, 然後將除最後 1 項的剩餘部分,調整為最大堆; 重複上面的操作。 ''' for index in range(n-1, 0, -1): swap(nums, 0, index) filter_down(nums, 0, index) def test(): nums0 = [1,2,3] nums1 = [1,3,2] nums2 = [2,1,3] nums3 = [2,3,1] nums4 = [3,1,2] nums5 = [3,2,1] nums6 = [5,1,6,3,4,8] nums7 = [6,1,8,3,4,5] for nums in (nums0, nums1, nums2, nums3, nums4, nums5, nums6, nums7): filter_down(nums, 0, len(nums)) assert nums0 == [3,2,1] assert nums1 == [3,1,2] assert nums2 == [3,1,2] assert nums3 == [3,2,1] assert nums4 == [3,1,2] assert nums5 == [3,2,1] assert nums6 == [6,1,8,3,4,5] assert nums7 == [8,1,6,3,4,5] nums = [7,4,5,3,8,9] heap_sort(nums) assert nums == [3,4,5,7,8,9] print('Pass!')
演算法複雜度
時間複雜度:
最壞情況下 ;
最好情況下 ;
平均情況 。
空間複雜度:
。
穩定性
。
參考文獻
- 《資料結構(第 2 版)》 - 浙江大學 - P148——P151、P265——P267;
- 《資料結構與演算法 Python 語言描述》 - 裘宗燕 - 北京大學 - ;
- https://github.com/henry199101/sort/blob/master/heap_sort.py。