排序算法總結之希爾排序


一,希爾排序算法介紹

①希爾排序又稱縮小增量排序 ,它本質上是一個插入排序算法。為什么呢?

因為,對於插入排序而言,插入排序是將當前待排序的元素與前面所有的元素比較,而希爾排序是將當前元素與前面增量位置上的元素進行比較,然后,再將該元素插入到合適位置。當一趟希爾排序完成后,處於增量位置上的元素是有序的。

②希爾排序算法的效率依賴於增量的選取

假設增量序列為 h(1),h(2).....h(k),其中h(1)必須為1,且h(1)<h(2)<...h(k) 。

第一趟排序時在增量為h(k)的各個元素上進行比較;

第二趟排序在增量為h(k-1)的各個元素上進行比較;

..........

最后一趟在增量h(1)上進行比較。由此可以看出,每進行一趟排序,增量是一個不斷減少的過程,因此稱之為縮小增量。

當增量減少到h(1)=1時,這里完全就是插入排序了,而在此時,整個元素經過前面的 k-1 趟排序后,已經變得基本有序了,而我們知道,對於插入排序而言,當待排序的數組基本有序時,插入排序的效率是非常高的。因此,希爾排序就是利用“增量”技巧將插入排序的平均時間復雜度O(N^2)降低為亞二次方。

 

二,希爾排序實例分析

假設原始數組為: 81   94   11   96   12   35    增量序列為 h(1)=1  h(2)=3

這里一共有兩個增量序列,故一共有兩趟排序。第一趟按照增量3來排序

處於增量3上的元素集合如下:<81      96>,<94      12>,<11      35>排序之后變成:<81      96>,<12      94>,<11      35>

即,數組變成了:81   12   11   96   94   35

可以看出,在增量為3的各個位置上的元素都是有序的。

經過前面一趟排序,此時數組已經基本有序,再使用增量為1的排序時(插入排序),比較的次數將會大大地降低。

第二趟按增量為h(1)=1 來排序,排序后數組變成:

11   12   35   81   94   96

 

三,算法實現

 1 public class ShellSort {
 2     
 3     /**
 4      * 使用希爾推薦的增量: h(k)=N/2 , h(i)=h(i+1)/2
 5      * @param arr 待排序數組
 6      */
 7     public static <T extends Comparable<? super T>> void shellSort(T[] arr){
 8         
 9         int j;
10         for(int gap = arr.length; gap >= 1; gap /=2)//排序的趟數
11         {
12         
13             /*每對增量序列為:
14              * <gap, 0> <gap+1,1> <gap+2,2>.....<arr.length-1, arr.length-1-gap>
15              */
16             for(int i = gap; i < arr.length; i++)//在"增量位置上" 進行插入排序
17             {
18                 T tmp = arr[i];
19                 for(j = i; j >= gap && tmp.compareTo(arr[j - gap]) < 0; j-= gap)
20                     arr[j] = arr[j - gap];
21                 arr[j] = tmp;
22             }
23         }
24     }
25     
26     //for test purpose
27     public static void main(String[] args) {
28         Integer[] arr = {81,94,11,96,12,35,17,95,28,58,41,75,15};
29         shellSort(arr);
30         for (Integer integer : arr) {
31             System.out.print(integer + " ");
32         }
33     }
34 }

①第10行for循環表示,一共有多少個增量。增量的序列的個數,就是希爾排序的趟數。

上面的增量序列為: arr.length/2 , arr.length/2/2, arr.length/2/2/2    ....   2,  1

②里層的兩個for循環(第16行至23行)實際上是一個插入排序

③第16行for循環表示:從數組的第 arr.length/2 下標起,對各組增量序列上的元素進行插入排序。gap等於arr.length/2時的排序分析如下:

對於第一趟排序而言(gap等於arr.length/2),一共有多少組呢?答案是一共有 arr.length - arr.length/2 + 1 組。

即:arr[arr.length/2]   arr[0] 這兩個元素是一組

arr[arr.length/2 + 1]   arr[1] 這兩個元素是一組

.....

arr[arr.length -1]   arr[arr.length-1-gap]這兩個元素是一組,此時gap等於arr.length/2

④第19行for循環,就是插入排序中的將當前元素與前面的元素進行比較。這里的前面元素是相差 gap 個位置上的元素。

比如, arr[length/2]  與 arr[length/2-gap]進行比較。

 

四:參考資料

排序算法總結之快速排序

排序算法總結之歸並排序

排序算法總結之堆排序

排序算法總結之插入排序

各種排序算法的總結

 

至此,五大基於內存的排序算法:插入排序、希爾排序;歸並排序、快速排序;堆排序 全部已經實現了一遍。

插入排序和希爾排序是一類,本質上希爾排序就是插入排序。插入排序它的增量就是1,而希爾排序則按多個增量進行排序,增量逐漸減少至1

歸並排序和快速排序很像。之所以像,是因為它們都基於分治、遞歸。歸並排序每次將原數組遞歸分成兩個大小相等的子數組,而快速排序則是基於pivot元素將原數組分成兩個子數組。歸並排序不斷地分解原數組,直到划分的兩個子數組中只存在一個元素時,這時,這兩個子數組可以視為都是有序的。從而,再合並兩個有序的子數組,來實現排序。

堆排序,借助了二叉堆,邏輯上是一棵完全二叉樹,物理存儲結構是一個一維數組。通過不斷地刪除堆頂元素,來實現排序。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM