思想導引
希爾排序是希爾(Donald Shell)於1959年提出的一種排序算法,它是基於簡單插入排序的改進版本,也稱爲遞減增量排序算法。
在食用本文之前可以先簡單瞭解一下插入排序的原理。
我們都知道插入排序的時間複雜度是,比如對於一個長度爲8的序列,它的時間複雜度對於插入排序來說就是。
但是需要注意的是實際上這是插入排序的最壞時間複雜度,如果該序列的原本順序就已經很好時,比如12345678
,插入排序就會達到一個最好的時間複雜度即。因此當一個序列原本的順序越工整時,它所需要的時間複雜度就會越小,接近於。
而如果將該序列先分爲兩組長度都爲4的序列,然後分別排序,則時間複雜度可以變爲,接下來再對整個長度爲8的序列進入插入排序,根據前面所講,此時的時間複雜度絕對是小於,甚至接近於,因此總的時間複雜度加起來是可以小於的,注意是可以小於,如何確保能夠小於還需要考慮以下問題。
首先要考慮,怎麼分組呢?一種簡單粗暴的方式就是切片分組,比如將這裏第1個到第4個的元素分爲一組,將第5個到第8個分爲另外一組,這樣的效果其實並不怎麼好,比如如果某個後面的切片分組比前面的某個分組整體元素的值都要小,後面對整個序列插入排序的時候需要“挪動”的元素也越多,時間複雜度會急劇上升,具體解釋略。而希爾採取的是增量分組,即1357爲一組,2468爲一組,這樣做的好處就是搜索範圍大,隨機性更好。
其次就是每次分組的組數問題,組數在希爾排序中等於步長,也就是後面程序中的gap。是分成8組,然後4組,2組,1組這樣依次除以2的方式,還是分成9,3,1依次除以3的方式呢?這篇博客中提到依次除以2的方式似乎跟簡單插入排序的時間複雜度一樣了,而依次除以3的方式似乎是可以的。因此如何優化增量序列也是希爾排序中的一個重點,具體這裏就不展開了。
算法概要
網上也有比較有趣的圖解示例,個人覺得維基百科上的就已經比較好理解了,摘抄如下。
假設有這樣一組數[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我們以步長爲5開始進行排序,我們可以通過將這列表放在有5列的表中來更好地描述算法,這樣他們就應該看起來是這樣:
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
然後我們對每列進行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
將上述四行數字,依序接在一起時我們得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].這時10已經移至正確位置了,然後再以3爲步長進行排序:
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
排序之後變爲:
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94
最後以1步長進行排序就可以了。
程序描述
採用python語言描述如下:
ef shell_sort(arr):
'''希爾排序
'''
import math
gap = 1 # 步長
while(gap < len(arr)/3):
gap = gap*3+1
while gap > 0:
# 從第一組的第二個元素開始插入排序
for i in range(gap, len(arr)):
temp = arr[i]
j = i-gap
while j >= 0 and arr[j] > temp:
arr[j+gap] = arr[j]
j -= gap
arr[j+gap] = temp
# 將組數減少爲3倍
gap = math.floor(gap/3)
return arr
需要注意的是這裏的初始步長是待排序數組長度的三分之一。
另外程序中體現的並不是像上面一樣的對每列分別插入排序,
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
即先插入第一列的25,45,10,然後插入第二列的59,27。爲了實現的方便,這裏程序是依次按照25,59,94,65,…即原本的數組順序依次插入的。
參考鏈接
爲什麼要註明參考鏈接?
- 尊重別人的思考,不喜歡無腦抄襲
- 不太可能通過一本書,單個博文就能徹底理解某個概念,方便所謂集思廣益