經典排序算法——希爾排序

注:本文參考https://www.cnblogs.com/chengxiao/p/6104371.html html

希爾排序原理

  在講解希爾排序以前,咱們有必要先回頭看一下插入排序的問題。插入排序無論數組分佈時怎麼樣的,都是一步步的對元素進行比較,移動,插入。好比[5,4,3,2,1,0]這種倒序序列,數組末端的0要回到首位很費勁,比較和移動元素均需n-1次。這時就引出了希爾排序。算法

  希爾排序是希爾(Donald Shell)於1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序通過改進以後的更高效的版本。該算法是突破O(n^2)的第一批算法之一。數組

  希爾排序是把記錄按下標的必定增量分組,對每組使用直接插入排序算法排序。隨着增量逐漸減少,每組包含的數字愈來愈多,當增量減至1時,整個文件剛好被分紅一組,算法便終止。dom

圖解希爾排序

  咱們來看下希爾排序的基本步驟,咱們選擇增量gap = length/2,縮小增量繼續以gap = gap/2 的方式,這種增量選擇咱們能夠用一個序列來表示,{n/2,(n/2)/2...1}的增量序列。性能

  原始數組 如下數據元素顏色相同的爲一組測試

  初始增量 gap = length/2 = 5,因此整個數組被分爲5組,即從第一位置的數字開始向後+n*gap爲同一組,因此第一次增量後5組爲[8,3],[9,5],[1,4],[7,6],[2,0]優化

  對這五組分別進行插入排序,例如[8,3]插入排序後會交換位置[3,8],[9,5]插入排序後會交換位置爲[5,9]等等。因此執行後會將小的數字放在數組的前面。spa

  而後繼續減少增量gap=5/2 = 2,數組被分爲2組,即從第一位置的數字開始向後+n*gap爲同一組,搜衣第二次增量後2組爲[3,1,0,9,7] 和 [5,6,8,4,2]。以下圖所示code

 

  對以上兩組再分別進行插入排序,在[3,1,0,9,7]中會變爲[0,1,3,7,9], [5,6,8,4,2]會變爲[2,4,5,6,8],以下圖所示,能夠看到整個數組的有序程度更進一步了。htm

  再縮小增來gap=2/2 = 1 ,此時整個數組爲1組[0,2,1,4,3,5,7,6,9,8],以下圖所示

   通過上面的「宏觀調控」,整個數組已經基本有序。此時再對整個數組進行插入排序,只需進行少許的交換便可變爲有序數組。

代碼實現

  1.交換式實現方式

 1     /**
 2      * 交換式排序
 3      * @param array
 4      */
 5 public static void sort1(int[] array){
 6         int temp = 0;
 7         int count = 0;
 8         for(int gap=array.length/2; gap >0; gap /= 2){
 9             for (int i = gap; i < array.length; i++) {
10                 int j = i - gap;
11                 while (j >=0 && array[j] > array[j+gap]){
12                     //所在分組需作交換
13                     temp = array[j];
14                     array[j] = array[j+gap];
15                     array[j+gap] = temp;
16                     j -= gap;
17                 }
18             }
19         }
20     }

  2.移位式實現方式

 1 /**
 2      * 移位式排序
 3      * @param array
 4      */
 5 public static void sort2(int[] array){
 6         for(int gap = array.length/2; gap > 0; gap /= 2){
 7             for(int i=gap; i< array.length; i++) {
 8                 int insertValue = array[i];
 9                 int insertIndex = i - gap;
10                 int startIndex = insertIndex;
11                 while (insertIndex >= 0 && insertValue < array[insertIndex]) {
12                     //所在分組需作後移
13                     array[insertIndex + gap] = array[insertIndex];
14                     insertIndex -= gap;
15                 }
16                 if(insertIndex != startIndex){
17                     array[insertIndex + gap] = insertValue;
18                 }
19             }
20         }
21     }

代碼分析

  1)第一層for循環用於肯定分組大小,這裏選用對數組大小減半的的方式,後面每次對原分組減半,直到分組大小爲1

  2)第二層for循環表示從第gap個元素開始(這裏說明下爲何從第gap個元素開始:第gap個元素恰好爲分組中的第一組的第二個元素,由於咱們的插入排序爲從第二個元素開始向前比較),向後依次執行插入排序,直到最後一個元素

  3)while循環用於對每一個分組進行插入排序(交換式爲當找到比插入元素大的元素時,交換兩個元素。移位式爲當找到比插入元素大時,將那個元素向後移動gap位,最後再將待插入元素放在空缺的位置便可)

時間複雜度

  從咱們的分析可知,希爾排序中對於增量序列的選擇十分重要,直接影響到希爾排序的性能。咱們上面選擇的增量序列{n/2,(n/2)/2...1},其最壞時間複雜度依然位O(n^2),一些通過優化的增量序列如Hibbard通過複雜證實可以使得最壞時間複雜度爲O(n3/2)。

測試算法執行效率

  與前面的排序算法相同,咱們依然生成10萬個隨機數的數組,使用插入排序方法進行排序,看其執行時間。測試代碼以下

1 public static void main(String []args){
2         int[] array = new int[1000000];
3         for (int i = 0; i < 1000000; i++) {
4             array[i] = (int) (Math.random() * 8000000);
5         }
6         long begin = System.currentTimeMillis();
7         sort2(array);
8         System.out.println("總耗時="+(System.currentTimeMillis()- begin));
9 }

測試結果

  能夠看到希爾排序算法比插入排序更快,10萬個數據的數組排序大概須要300多毫秒時間。下篇咱們將介紹快速排序算法,排序效率是否會更高呢?一塊兒期待!

總結

  希爾排序是一個基於插入排序的算法,解決了當待排序數組倒序時使用插入排序算法要移動屢次的耗時操做。希爾排序的核心算法爲先將待排序數組進行分組進行小範圍的排序,每次分組排序後待排序數組就會在大範圍上更有序。當分組大小減少到1時,待排序數組已經基本有序,此時再執行插入排序,能夠確保移動或交換的次數更少,從而達到提高排序效率的目的。

 

注:本文參考https://www.cnblogs.com/chengxiao/p/6104371.html 

相關文章
相關標籤/搜索