排序算法系列:Shell 排序算法
概述
希爾排序(Shell Sort)是 D.L.Shell 于 1959 年提出來的一種排序算法,在這之前排序算法的時間復雜度基本都是 O(n2n^{2}n2) 的,希爾排序算法是突破這個時間復雜度的第一批算法之一。希爾排序是一種插入排序算法。
版權說明
著作權歸作者所有。
商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
本文作者:Q-WHai
發(fā)表日期: 2016年4月12日
本文鏈接:https://qwhai.blog.csdn.net/article/details/51127533
來源:CSDN
更多內(nèi)容:分類 >> 算法與數(shù)學
目錄
文章目錄
- 概述
- 版權說明
- 目錄
- @[toc]
- 算法簡介
- 算法原理分析
- 算法步驟
- 邏輯實現(xiàn)
- 復雜度分析
- 問題解答
- Ref
- Github源碼下載
- 征集
- @[toc]
算法簡介
希爾排序(Shell Sort)是 D.L.Shell 于 1959 年提出來的一種排序算法,在這之前排序算法的時間復雜度基本都是 O(n2n^{2}n2) 的,希爾排序算法是突破這個時間復雜度的第一批算法之一。
– 《大話數(shù)據(jù)結構》
算法原理分析
在上一篇《排序算法系列:插入排序算法》一文中,我們說到了一種復雜度為 O(n2n^{2}n2) 的排序算法。而對于插入排序而言,如果我們的排序序列基本有序,或是數(shù)據(jù)量比較小,那么它的排序性能還是不錯的。為什么?可能你會這樣問。數(shù)據(jù)量小,這個不用多說,對于多數(shù)排序算法這一點都適用;如果一個序列已經(jīng)基本有序(序列中小數(shù)普遍位于大數(shù)的前面),那么我們在插入排序的時候,就可以在較少的比較操作之后使整體有序。如果,這一點你還不是很明白,可以先看看《排序算法系列:插入排序算法》一文中的解釋。
基于上面基本有序和小數(shù)據(jù)量的提示,這里就有了希爾排序的實現(xiàn)。希爾所做的就是分組,在每個分組中進行插入排序。如下就是希爾排序中分組的示意圖:

在上圖中,我們將一個長度為 10 的序列,分組成 3 組。除最后一組以外的分組都包含了 4 個元素。可是,我們排序的時候并不是在這些分組內(nèi)部進行的。而是我們要按照上面圓形的標號來進行插入排序,也就是排序中的 [4, 9, 7]。你可以先想一想這是為什么,在文章的最后,我再說明這個問題。
上面從詳細地說明了希爾排序的由來及希爾排序的分組精髓。那么現(xiàn)在就要說希爾排序中的插入排序,是的沒有錯。畢竟希爾排序的核心就是分組+插入排序嘛。在這個示意圖中,我們可以很明顯地看出它想表達的東西。這里只是將[3, 9, 7]看成了一個整體,對于其它的元素,暫時與[3, 9, 7]無關。
通過上面的兩步操作,我們可以得到一個序列 T1 = [4, 0, 6, 1, 7, 2, 8, 5, 9, 3]。咦,這個序列還是無序的呀,怎么回事?我們說希爾排序的核心是分組和獲得一個基本有序的數(shù)組。這里說的是基本有序,并未做出承諾說這個序列已經(jīng)有序。那么,現(xiàn)在要做的就是繼續(xù)分組+插入排序。當然,對應的 step 是要進行修改的。詳細過程,參見下面的算法步驟。
算法步驟
通過上面的算法原理分析,這里可以總結出算法實現(xiàn)的步驟。如下:
邏輯實現(xiàn)
這是Shell 排序的核心模塊,也是分組的關鍵步驟
private void core(int[] array) {int arrayLength = array.length;int step = arrayLength;do {step = step / 3 + 1;for (int i = 0; i < step; i++) {insert(array, i, step);}System.err.println(Arrays.toString(array));} while (step > 1);}希爾排序的直接插入排序,注意這里不同的是它只是針對某一個分組子序列進行插入排序
private void insert(int[] array, int offset, int step) {int arrayLength = array.length;int groupCount = arrayLength / step + (arrayLength % step > offset ? 1 : 0);for (int i = 1; i < groupCount; i++) {int nextIndex = offset + step * i;int waitInsert = array[nextIndex];while(nextIndex - step >= 0 && waitInsert < array[nextIndex - step]) {array[nextIndex] = array[nextIndex - step];nextIndex -= step;}array[nextIndex] = waitInsert;}}更多詳細邏輯實現(xiàn),請參見文章結尾的源碼鏈接。
復雜度分析
| 排序方法 | 時間復雜度 | 空間復雜度 | 穩(wěn)定性 | 復雜性 | ||
| 平均情況 | 最壞情況 | 最好情況 | ||||
| Shell 排序 | O($n^{3/2}$) | O($n^{2}$) | O(n) | O(n) | 不穩(wěn)定 | 較復雜 |
問題解答
答:這里我們分組的目的在于要完成的是對整個序列的基本序列處理,那么我們肯定想要讓這些分組的數(shù)據(jù)盡量地分散開。如果要針對每個分組內(nèi)部進行插入排序,那么之后的每次操作,都會是在內(nèi)部進行的,這樣的結果就是每個分組內(nèi)部已經(jīng)有序,只是整體仍然是無序的。
答:這里的步長計算很關鍵,可是究竟應該選取什么樣的增量才是最好的,目前還尚未解決。不過大量的研究表明,當增量序列為 dlta[k] = 2t?k+1{2^{t-k+1}}2t?k+1 - 1 (0 <= k <= t <= [log2log_{2}log2?(n+1)])時,可以獲得不錯的效果,其時間復雜度為 O(n3/2n^{3/2}n3/2)。
Ref
- 《大話數(shù)據(jù)結構》
Github源碼下載
- https://github.com/qwhai/algorithms-sort
征集
如果你也需要使用ProcessOn這款在線繪圖工具,可以使用如下邀請鏈接進行注冊:
https://www.processon.com/i/56205c2ee4b0f6ed10838a6d
總結
以上是生活随笔為你收集整理的排序算法系列:Shell 排序算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单例模式在多线程中的安全性研究
- 下一篇: 交互式数据包处理程序 Scapy 入门指