上次說到了經典演算法選擇排序,感覺是比較簡單的演算法,這一次說一說稍微有點難度的堆排序。
堆排序的時間複雜度要明顯優於前面的氣泡排序,插入排序和選擇排序(侷限於n較大時)。
先來講講堆(二叉堆),是乙個陣列,它可以近似被看作是乙個完全二叉樹。樹上每乙個節點對應乙個元素,除了最底層外,該樹是完全充滿的,而且是從左至右填充的,所有最底層的元素會從左向右填充。表示堆的陣列list包括兩個屬性,list.length表示陣列的個數,list.heap_size則表示有多少個堆元素儲存在該陣列中。也就是0<=list.heap_size<=list.length,list中可能存在若干個元素不是堆元素,在講堆性質的時候會舉例說明。
二叉堆有兩種形式,最大堆和最小堆,但其實本身性質是一樣的,就和從小到大排序和從大到小排序一樣。
最大堆性質是指除了根以外的所有節點,都要滿足:
list[parent(i)] >= list[i]
最小堆性質要滿足:
list[parent(i)] <= list[i]
如果把堆看作是一棵樹,堆高度就是指的堆中節點的高度,即為根節點到葉節點最長簡單路徑上的數目。
對於乙個元素數量為n的list,其堆高度為floor(lg n)。
下面來證明一下:在高度為h的堆中,元素最多的應該是當底層全滿時,即:1+2+2^2+……+2^h=2^(h+1)-1
元素最少的應該是當底層只有乙個元素時,即:1+2+……+2^(h-1)+1=2^h
則有:2^h<=n<=2^(h+1)-1,分別對不等式裡面取對數lg,既有lg(2^h)<=lg n<=lg(2^(h+1)-1),則有h<=lg n
其實堆排序就只有兩種過程,乙個是建堆,乙個是堆的維護,建堆的歷遍的過程,而維護則是遞迴的過程。在這裡維護是底層,而歷遍是頂層。我們先來看看維護,以求解最大堆為例,先定義乙個初始化的根節點,這裡我們選擇root=length//2,來作為初始化值,left=2*root(左結點),right=left+1(右結點),堆維護實質上就是通過不斷的交換順序來保證堆的性質。即判斷根結點與左結點、右結點的最大值,如果左結點的值最大,則與根結點交換位置,若是右結點的值最大,則與根結點交換位置,否則該結點即為根結點,左結點,右結點的最大值,傳遞根結點下標到下一次遞迴,直到找到list中的最大根結點。而頂層迴圈則是倒序歷遍lenth//2,這樣足以完成所有元素的呼叫,甚至可以為(length-2)//2,終止條件是當根結點的下標為0時,結點1已經為最大堆的根節點了,此時迴圈結束。但是到這一步還不夠,這一步僅僅是能確保下標為0的結點1為根結點,即只能確保list[0]為根節點,通過講該根節點與list[list.szie]進行互換,去掉根節點,形成乙個新堆,長度為length-=1,構造新堆後,在利用堆維護性質來獲取新堆的根節點,依次的我們可以結點提取出來,形成乙個序列,這就是我們要求的序列。以下是用python實現經典插入排序的code。這裡的時間複雜度為omiga(n*lg n)
def heapsort(list):
if list!=none:
if list==1:
pass
else:
for start in range((len(list))//2,-1,-1):#頂層迴圈第一步,找到堆的根結點
rootsort(list,start,len(list)-1)
for end in range(len(list)-1,-1,-1):#頂層迴圈第二步,講根結點依次提取並排序
list[0],list[end]=list[end],list[0]
end-=1
rootsort(list,0,end)
print (list)
def rootsort(list,root,end):#遞迴函式,對list做最大堆調整
left=2*root #父結點的左結點
right=left+1#父結點的右結點
if left<=end and list[root]
out:[1, 2, 4, 4, 7, 8, 9, 10, 14, 16]
上面過程可以用網路上的一張進行描述:
這裡我再詳細將每一次求根結點過程展示一下:
def heapsort(list):
if list!=none:
if list==1:
pass
else:
for start in range((len(list))//2,-1,-1):
print(start)
rootsort(list,start,len(list)-1)
for end in range(len(list)-1,-1,-1):
list[0],list[end]=list[end],list[0]
print ('*',end,'*')
print ('^',list,'^')
end-=1
rootsort(list,0,end)
print (list)
def rootsort(list,root,end):
left=2*root
right=left+1
if left<=end and list[root]out:
543" 3 "
[4, 1, 4, 14, 16, 9, 10, 2, 8, 7]
2" 2 "
[4, 1, 16, 14, 4, 9, 10, 2, 8, 7]
" 4 "
[4, 1, 16, 14, 8, 9, 10, 2, 4, 7]
1" 1 "
[4, 16, 1, 14, 8, 9, 10, 2, 4, 7]
" 2 "
[4, 16, 9, 14, 8, 1, 10, 2, 4, 7]
0" 0 "
[16, 4, 9, 14, 8, 1, 10, 2, 4, 7]
" 1 "
[16, 14, 9, 4, 8, 1, 10, 2, 4, 7]
" 3 "
[16, 14, 9, 10, 8, 1, 4, 2, 4, 7]
* 9 *
^ [7, 14, 9, 10, 8, 1, 4, 2, 4, 16] ^
" 0 "
[14, 7, 9, 10, 8, 1, 4, 2, 4, 16]
" 1 "
[14, 10, 9, 7, 8, 1, 4, 2, 4, 16]
* 8 *
^ [4, 10, 9, 7, 8, 1, 4, 2, 14, 16] ^
" 0 "
[10, 4, 9, 7, 8, 1, 4, 2, 14, 16]
" 1 "
[10, 9, 4, 7, 8, 1, 4, 2, 14, 16]
" 2 "
[10, 9, 8, 7, 4, 1, 4, 2, 14, 16]
* 7 *
^ [2, 9, 8, 7, 4, 1, 4, 10, 14, 16] ^
" 0 "
[9, 2, 8, 7, 4, 1, 4, 10, 14, 16]
" 1 "
[9, 8, 2, 7, 4, 1, 4, 10, 14, 16]
" 2 "
[9, 8, 4, 7, 2, 1, 4, 10, 14, 16]
* 6 *
^ [4, 8, 4, 7, 2, 1, 9, 10, 14, 16] ^
" 0 "
[8, 4, 4, 7, 2, 1, 9, 10, 14, 16]
" 1 "
[8, 7, 4, 4, 2, 1, 9, 10, 14, 16]
* 5 *
^ [1, 7, 4, 4, 2, 8, 9, 10, 14, 16] ^
" 0 "
[7, 1, 4, 4, 2, 8, 9, 10, 14, 16]
" 1 "
[7, 4, 1, 4, 2, 8, 9, 10, 14, 16]
" 2 "
[7, 4, 2, 4, 1, 8, 9, 10, 14, 16]
* 4 *
^ [1, 4, 2, 4, 7, 8, 9, 10, 14, 16] ^
" 0 "
[4, 1, 2, 4, 7, 8, 9, 10, 14, 16]
" 1 "
[4, 4, 2, 1, 7, 8, 9, 10, 14, 16]
* 3 *
^ [1, 4, 2, 4, 7, 8, 9, 10, 14, 16] ^
" 0 "
[4, 1, 2, 4, 7, 8, 9, 10, 14, 16]
" 1 "
[4, 2, 1, 4, 7, 8, 9, 10, 14, 16]
* 2 *
^ [1, 2, 4, 4, 7, 8, 9, 10, 14, 16] ^
" 0 "
[2, 1, 4, 4, 7, 8, 9, 10, 14, 16]
* 1 *
^ [1, 2, 4, 4, 7, 8, 9, 10, 14, 16] ^
* 0 *
^ [1, 2, 4, 4, 7, 8, 9, 10, 14, 16] ^
[1, 2, 4, 4, 7, 8, 9, 10, 14, 16]
Python實現經典排序演算法
import random lis list range 100 random.shuffle lis print lis def bubblesort arr for i in range 1 len arr for j in range 0 len arr i if arr j arr j 1 ...
Python 實現經典排序演算法
穩定的排序演算法 氣泡排序 插入排序 歸併排序和基數排序。不是穩定的排序演算法 選擇排序 快速排序 希爾排序 堆排序。a 80,15,45,35,88,46,55,66,22,99,0,1,100 for j in range 0,len a for i in range 0,len a 1 if ...
Python實現經典排序演算法 氣泡排序
1.氣泡排序概述 氣泡排序是一種簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢 浮 到數列的頂端。2.演算法思想 比較相鄰...