本文是對 swift algorithm club 翻譯的一篇文章。希爾排序是插入排序的一種更高效的改進版本,方法是將原始列表分成較小的子列表,然後使用插入排序對其進行單獨排序。swift algorithm club是 raywenderlich.com**出品的用swift實現演算法和資料結構的開源專案,目前在github上有18000+⭐️,我初略統計了一下,大概有一百左右個的演算法和資料結構,基本上常見的都包含了,是ioser學習演算法和資料結構不錯的資源。
?andyron/swift-algorithm-club-cn是我對swift algorithm club,邊學習邊翻譯的專案。由於能力有限,如發現錯誤或翻譯不妥,請指正,歡迎pull request。也歡迎有興趣、有時間的小夥伴一起參與翻譯和學習?。當然也歡迎加⭐️,?????。
本文的翻譯原文和**可以檢視?swift-algorithm-club-cn/shell sort
插入排序是比較相連的元素,如果它們順序不對就交換它們,而希爾排序演算法會比較相距很遠的元素。
元素之間的距離稱為 gap。 如果被比較的元素的順序錯誤,則它們會在 gap 中交換。 這消除了插入排序中常見的許多中間副本。
這個想法是,通過在大 gap 上移動元素,陣列變得非常快速地部分排序。 這使得之後的排序過程更快,因為他們不再需要交換那麼多項。
一輪完成後,gap變小,新一輪開始。 這將重複,直到 gap 大小為1,此時演算法的功能就像插入排序一樣。 但是由於資料已經很好地排序,所以最後一輪可以非常快。
假設我們想使用希爾排序對陣列[64, 20, 50, 33, 72, 10, 23, -1, 4]
進行排序。
我們首先將陣列的長度除以2:
n = floor(9/2) = 4
這是 gap 大小。
我們建立n
子列表。 在每個子列表中,每一項的間隔是大小為n
的gap 。 在我們的示例中,我們需要製作其中四個子列表。 子列表按insertionsort()
函式排序。
這可能沒有多大意義,所以讓我們仔細看看會發生什麼。
第一輪如下。 我們有n = 4
,所以我們製作了四個子列表:
sublist 0: [ 64, xx, xx, xx, 72, xx, xx, xx, 4 ]
sublist 1: [ xx, 20, xx, xx, xx, 10, xx, xx, xx ]
sublist 2: [ xx, xx, 50, xx, xx, xx, 23, xx, xx ]
sublist 3: [ xx, xx, xx, 33, xx, xx, xx, -1, xx ]
如您所見,每個子列表僅包含原始陣列中的每間隔4的項。 不在子列表中的項用xx
表示。 所以第乙個子列表是[64,72,4]
,第二個子列表是[20,10]
,依此類推。 我們使用這個「gap」的原因是我們不必實際製作新的陣列。 相反,我們將它們交織在原始陣列中。
我們現在在每個子列表上呼叫一次insertionsort()
。
插入排序的這個特定版本從後面到前面排序。子列表中的每個專案都與其他專案進行比較。如果它們的順序錯誤,則交換值並一直向下移動,直到我們到達子列表的開頭。
因此對於子列表0,我們將4
與72
交換,然後將4
與64
交換。 排序後,此子列表如下所示:
sublist 0: [ 4, xx, xx, xx, 64, xx, xx, xx, 72 ]
排序後的其他三個子列表:
sublist 1: [ xx, 10, xx, xx, xx, 20, xx, xx, xx ]
sublist 2: [ xx, xx, 23, xx, xx, xx, 50, xx, xx ]
sublist 3: [ xx, xx, xx, -1, xx, xx, xx, 33, xx ]
完整的陣列看上去是:
[ 4, 10, 23, -1, 64, 20, 50, 33, 72 ]
它還沒有完全排序,但它比以前更加排序。 這完成了第一次輪操作。
在第二輪中,我們將 gap 大小除以2:
n = floor(4/2) = 2
這意味著我們現在只建立兩個子列表:
sublist 0: [ 4, xx, 23, xx, 64, xx, 50, xx, 72 ]
sublist 1: [ xx, 10, xx, -1, xx, 20, xx, 33, xx ]
每個子列表包含每個間隔為2的項。 我們再次呼叫insertionsort()
來對這些子列表進行排序。 結果是:
sublist 0: [ 4, xx, 23, xx, 50, xx, 64, xx, 72 ]
sublist 1: [ xx, -1, xx, 10, xx, 20, xx, 33, xx ]
請注意,在每個列表中只有兩個元素位置順序不對(譯註:sublist 0是64和50,sublist 1是10和-1)。 因此插入排序非常快。 那是因為我們已經在第一輪中對陣列進行了一些排序。
總數組現在看起來像這樣:
[ 4, -1, 23, 10, 50, 20, 64, 33, 72 ]
這樣就完成了第二輪。 最後一輪的gap是:
n = floor(2/2) = 1
gap 大小為1表示我們只有乙個子列表,即陣列本身,我們再次呼叫insertionsort()
對其進行排序。 最終排序的陣列是:
[ -1, 4, 10, 20, 23, 33, 50, 64, 72 ]
在大多數情況下,希爾排序的效能為o(n^2),如果幸運,則為o(nlogn)。 該演算法是不穩定的排序; 它可能會改變具有相等值的元素的相對順序。
「 gap 序列」確定 gap 的初始大小以及每次迭代如何使 gap 變小。 良好的 gap 序列對於希爾排序表現良好非常重要。
上面實現例子中的 gap 序列是希爾原始版本中的 gap 序列:初始值是陣列大小的一半,然後每次除以2。 還有其他方法可以計算 gap 序列。
這是 matthijs 很久以前使用的乙個舊的commodore 64 basic版本的希爾排序,並且移植到他曾經使用的幾乎所有程式語言中:
61200 rem s is the array to be sorted
61205 rem as is the number of elements in s
61210 w1=as
61220 if w1<=0 then 61310
61230 w1=int(w1/2): w2=as-w1
61240 v=0
61250 for n1=0 to w2-1
61260 w3=n1+w1
61270 if s(n1)>s(w3) then sh=s(n1): s(n1)=s(w3): s(w3)=sh: v=1
61280 next n1
61290 if v>0 then 61240
61300 goto 61220
61310 return
希爾排序的swift實現:
public func insertsort(_ list: inout[int], start: int, gap: int)
list[pos] = currentvalue
}}public func shellsort(_ list: inout [int]) {
var sublistcount = list.count / 2
while sublistcount > 0 {
for pos in 0..希爾排序的維基百科
rosetta code的希爾排序(譯註:大概70種不同語言實現希爾排序??)
翻譯:andy ron
校對:andy ron
希爾排序,也稱遞減增量排序演算法,按其設計者希爾(donald shell)的名字命名,在2023年公布。
希爾排序是將待排序的陣列元素按下標的一定增量分組 ,分成多個子串行,然後對各個子串行進行直接插入排序演算法排序;然後依次縮減增量再進行排序,直到增量為1時,進行最後一次直接插入排序,排序結束。
**排序演算法(二)之希爾排序
排序:希爾排序(演算法)
排序演算法 希爾排序
如果乙個排序演算法,每次只把諸專案移動乙個位置,則它的平均執行時間至少要和n2成比例.因為在這個排序演算法執行的過程當中,每個記錄平均都必須遍歷n 3個位置,因此如果要對直接插入排序進行有效的,實質性的改進的話,就要有一種演算法,它可以使記錄做長距離的跳躍,而不是一步一步的挪動.希爾排序也是一種插入...
排序演算法 希爾排序
摘要 排序演算法有很多,最簡單的有氣泡排序和插入排序,這兩種方法都具有o n 2 的時間界.我們想要討論的是具有更好的時間界的排序演算法,比如希爾排序.1 希爾排序的思路是通過比較一定間距的元素來進行排序,最後再對所有相鄰元素進行一次插入排序.2 希爾排序最重要的引數是增量序列 h1,h2,ht 只...
排序演算法 希爾排序
希爾排序,也稱遞減增量排序演算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序演算法。希爾排序通過將比較的全部元素分為幾個區域來提公升插入排序的效能。這樣可以讓乙個元素可以一次性地朝最終位置前進一大步。然後演算法再取越來越小的步長進行排序,演算法的最後一步就是普通的插入排序,但是到了這步,...