希爾排序
希爾排序是D.L.shell於1959年提出來的排序算法,在這之前(見排序1),排序算法的時間複雜度基本都是O(n^2),希爾排序是突破這個時間複雜度的第一批算法之一。
基本原理
上面講的插入排序,在記錄本身比較有序或者當記錄較少時,比較高效。而希爾排序就是先使記錄基本有序,然後再對全體記錄進行一次插入排序。所謂基本有序,就是小的關鍵字基本在前面,大的基本在後面,不大不小的基本在中間,像{2,1,3,6,4,7,5,8,9}這樣的就可以稱爲基本有序了。但像{1,5,9,3,7,8,2,4,6}這樣的9在第三位,2在倒數第三位就談不上基本有序
python代碼
def shell_sort(arr):
count = len(arr)
step = 2
group = int(count / step)
while group > 0:
for i in range(0,group):
j = i + group
while j < count:
k = j - group
key = arr[j]
while k >= 0:
if arr[k] > key:
arr[k+group] = arr[k]
arr[k] = key
k -= group
j += group
group = int(group/step)
return arr
希爾排序算法
考慮到是記錄基本有序,可以採用增量比較法,arr[i]先不直接與arr[i+1]比較,而是與arr[i+increment]比較,這個increment就稱之爲增量,在python代碼中用group表示。假設初始記錄arr爲{9,1,5,8,3,7,4,6,2},初始增量group=4
i = 0時,比較arr[0]=9與arr[4]=3的大小,因爲arr[0]>arr[4],所以交換9,3位置,記錄爲{3,1,5,8,9,7,4,6,2}
同時比較arr[4]>arr[8],故交換,arr[0]>arr[4],交換,得到記錄{2,1,5,8,3,7,4,6,9}
i= 1時,比較arr[1]=1與arr[5]=7的大小,前者小於後者,不交換
i= 2時,比較arr[2]=5與arr[6]=4的大小,前者大於後者,故交換,記錄爲{2,1,4,8,9,7,5,6,9}
i= 3時,比較arr[3]=8與arr[7]=6的大小,前者大於後者,故交換,記錄爲{2,1,4,6,3,7,5,8,9}
此時序列已經達到基本有序,接下來減少增量group=2,循環一輪後,再次縮小group=1,便可以使記錄達到有序
希爾排序複雜度分析
經過上面剖析,發現希爾排序是將相隔某個"增量"的記錄組成一個子序列,實現移動式的跳躍,使得效率升高,那麼增量的選擇就十分重要,當增量序列爲2^(t+k+1)-1時,可以獲得不錯效果,時間複雜度是O(n^(3/2)),優於直接排序,另外希爾排序並不是一種穩定的排序算法
堆排序
之前的簡單選擇排序,選取最小的記錄需要比較n-1次,可惜這樣的操作並沒有把每一趟的結果都保存下來,後面的比較中又執行了一次,因而記錄的比較次數較多。如果能做到在每次選擇最小記錄的同時,根據比較結果對其他記錄做出相應調整,那麼總體的效率就會非常高了。
基本概念
堆是具有下列性質的完全二叉樹:每個節點的值都大於或等於其左右孩子結點的值,稱爲大頂堆(下圖左);或每個節點的值都小於或等於其左右孩子結點的值,稱爲小頂堆(下圖右)
堆排序算法
將待排序的序列構成一個大頂堆。此時,整個序列的最大值就是堆頂的根節點。將它移走(其實就是將其與堆數組的末尾元素交換,此時末尾元素就是最大值),然後將剩餘的n-1個序列重新構造一個堆,這樣就會得到n個元素中次小值。如此反覆執行,便能得到一個有序序列了。
python代碼
def heap_sort(lst):
for start in range((len(lst) - 2) // 2, -1, -1):
siftdown(lst, start, len(lst) - 1)
for end in range(len(lst) - 1, 0, -1):
lst[end], lst[0] = lst[0], lst[end]
siftdown(lst, 0, end - 1)
return lst
def siftdown(lst, start, end):
root = start
while True:
child = 2 * root + 1
if child > end: break
if child + 1 <= end and lst[child] < lst[child + 1]:
child += 1
if lst[child] > lst[root]:
lst[child], lst[root] = lst[root], lst[child]
root = child
else:
break
heap_sort(l)
歸併排序(Merging Sort)
基本思想: 利用歸併的思想實現的排序方法。它的原理是假設初始序列含有n個記錄,則可以看成n個有序的子序列,每個子序列的長度爲1,然後兩兩歸併,得到[n/2]個長度爲2或1的有序子序列;再兩兩歸併,......,如此重複,直至得到一個長度爲n的有序序列爲止,這種排序方法稱爲2路歸併排序。
排序過程
圖來自《大話數據結構》
python代碼
def merge_sort(arr):
if len(arr) <= 1: #子序列
return arr
mid = (len(arr) // 2)
left = merge_sort(arr[:mid])#遞歸的切片操作
right = merge_sort(arr[mid:len(arr)])
result = []
while len(left) > 0 and len(right) > 0:
if (left[0] <= right[0]):
result.append(left.pop(0))
else:
result.append(right.pop(0))
#j+= 1
if (len(left) > 0):
result.extend(merge_sort(left))
else:
result.extend(merge_sort(right))
return result
merge_sort(a)
複雜度分析
一趟歸併需要將記錄中相鄰的長度爲h的有序序列進行兩兩歸併,這需要將待排序序列中的所有記錄掃描一遍,因此需要耗費
O(n)時間,而由完全二叉樹的深度可知,整個歸併排序需要進行logn次,因此,總的時間複雜度爲O(nlogn),而且這是歸併排序算法中最好、最壞、平均的時間性能。空間複雜度爲O(n+logn)
歸併排序不存跳躍,因此算是一種穩定的排序算法