每日一問6.22

堆排序的python實現

思路分析:

  1. 使用列表存儲
  2. 從後向前調用堆調整算法建立最大堆,使用O(nlogn)
  3. 交換首尾元素
  4. 調整前n-1個元素成最大堆,使用O(logn)
  5. 交換前n-1個元素的首尾元素
  6. 調整前n-2個元素成最大堆
  7. 交換前n-2個元素的首尾元素
  8. 直到調整前2個元素成最大堆
  9. 交換前2個元素的首尾元素
def exchange(alist, i, j):
    alist[i], alist[j] = alist[j], alist[i]
    
def build_max_heap(alist):
    for i in range(len(alist)-1, -1, -1):
        tune_heap(alist, i, len(alist))

def tune_heap(alist, start, max_index_one):
    left = 2 * start + 1
    right = left + 1
    if right<max_index_one:
        tmp = right if alist[right]>alist[left] else left
    elif left<max_index_one:
        tmp = left
    else:
        return
    if alist[start]<alist[tmp]:
        exchange(alist, start, tmp)
        tune_heap(alist, tmp, sub_length)
    
def sort(alist):
    build_max_heap(alist)
    for i in range(1, len(alist)):
        exchange(alist, 0, -1*i) # 最後一次交換是交換前2個元素
        tune_heap(alist, 0, len(alist)-i))


if __name__=="__main__":
    alist = [22, 3, 5, 8, 0]
    alist = []
    alist = [1]
    alist = [2,1]
    sort(alist)
    print(alist)

實現中的坑:

  1. exchange的實現中必須使用列表作爲參數,不可以使用普通變量,函數不會改變不可變的實參,但是可以改變可變實參,列表是可變實參。
  2. tune_heap的實現中不可以使用列表的子切片作爲實參,必須使用原始的整個列表,因爲列表的切片是淺拷貝,改變淺拷貝不影響原始列表。
  3. 節點i的左節點index是2*i+1不是2*i,因爲列表編號從0開始。

要點:

  1. 建最大堆,僅僅從後向前與父節點交換是不行的,因爲不能保證任意節點的值一定不大於父節點的值,比如alist=[22, 3, 44, 0, 66],第二大的元素44不會跑到第二層去(它會跑到最後去)。需要從後向前不斷調用調整堆算法。
  2. 調整堆中,在左右子節點存在一個時,使用tmp變量保存值最大的那個子節點索引是比較好的方法 。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章