堆排序的python實現
思路分析:
- 使用列表存儲
- 從後向前調用堆調整算法建立最大堆,使用
O(nlogn)
- 交換首尾元素
- 調整前n-1個元素成最大堆,使用
O(logn)
- 交換前n-1個元素的首尾元素
- 調整前n-2個元素成最大堆
- 交換前n-2個元素的首尾元素
- 直到調整前2個元素成最大堆
- 交換前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)
實現中的坑:
- exchange的實現中必須使用列表作爲參數,不可以使用普通變量,函數不會改變不可變的實參,但是可以改變可變實參,列表是可變實參。
- tune_heap的實現中不可以使用列表的子切片作爲實參,必須使用原始的整個列表,因爲列表的切片是淺拷貝,改變淺拷貝不影響原始列表。
- 節點
i
的左節點index是2*i+1
不是2*i
,因爲列表編號從0開始。
要點:
- 建最大堆,僅僅從後向前與父節點交換是不行的,因爲不能保證任意節點的值一定不大於父節點的值,比如
alist=[22, 3, 44, 0, 66]
,第二大的元素44
不會跑到第二層去(它會跑到最後去)。需要從後向前不斷調用調整堆算法。 - 調整堆中,在左右子節點存在一個時,使用
tmp
變量保存值最大的那個子節點索引是比較好的方法 。