1. 程式人生 > >希爾排序實現與複雜度、穩定性分析

希爾排序實現與複雜度、穩定性分析

import java.util.*;

public class ShellSort {
    public int[] shellSort(int[] a, int n) {
		//先判斷條件
        if(a== null|| n<2){
            return a;
        }
        
        int feet = n/2;
        int index =0;
        while(feet > 0){
            for(int i=feet;i<n;i++){
            	index = i;
                while(index >= feet){
                    if(a[index] <a[index-feet]){
                        swap(a,index,index-feet);
                     	index-=feet;   
                    }else{
                        break;
                    }
                }
                
            }
            feet = feet/2;
        }
         return a;
    }
    public void swap(int[] a,int big,int little){
        		int temp  = a[little];
        		a[little] = a[big];
        		a[big] = temp;
        
	}
}

希爾排序(Shell Sort)又叫做縮小增量排序(diminishing increment sort),是一種很優秀的排序法,演算法本身不難理解,也很容易實現,而且它的速度很快。

Shell排序通過將資料分成不同的組,先對每一組進行排序,然後再對所有的元素進行一次插入排序,以減少資料交換和移動的次數。

希爾排序是按照不同步長對元素進行插入排序,當剛開始元素很無序的時候,步長最大,所以插入排序的元素個數很少,速度很快;當元素基本有序了,步長很小,插入排序對於有序的序列效率很高。

由於多次插入排序,我們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最後其穩定性就會被打亂,所以,Shell排序是不穩定的。

插入排序(Insertion Sort)的一個重要的特點是,如果原始資料的大部分元素已經排序,那麼插入排序的速度很快(因為需要移動的元素很少)。從這個事實我們可以想到,如果原始資料只有很少元素,那麼排序的速度也很快。--希爾排序就是基於這兩點對插入排序作出了改進。

例如,有100個整數需要排序。

  1. 第一趟排序先把它分成50組,每組2個整數,分別排序。
  2. 第二趟排序再把經過第一趟排序後的100個整數分成25組,每組4個整數,分別排序。
  3. 第三趟排序再把前一次排序後的數分成12組,第組8個整數,分別排序。
  4. 照這樣子分下去,最後一趟分成100組,每組一個整數,這就相當於一次插入排序。

由於開始時每組只有很少整數,所以排序很快。之後每組含有的整數越來越多,但是由於這些數也越來越有序,所以排序速度也很快。

希爾排序平均效率是O(nlogn)。其中分組的合理性會對演算法產生重要的影響。現在多用D.E.Knuth的分組方法。下面來分析原始的希爾排序的時間複雜度,初始步長d=n/2,下一次步長d=d/2

第一次比較次數,每組2個元素:1*n/2

第二次比較次數,每組4個元素:最壞(1+2+3)*n/4

第三次比較次數,每組8個元素:最壞(1+2+3+……+7)*n/8

... ...

2^(m-1) ( m 表示第幾次比較 ) < 每組最壞的元素比較次數 < 2^(m) 

例子 :2^2 < 7 < 2^3 (第 3 次比較,最後一個元素的最差比較次數 )

累加求極限,得到演算法複雜度小於 O(n^2) 。

詳見:http://blog.csdn.NET/ginnosx/article/details/12263619
Shell排序比氣泡排序快5倍,比插入排序大致快2倍。Shell排序比起QuickSort,MergeSort,HeapSort慢很多。但是它相對比較簡單,它適合於資料量在5000以下並且速度並不是特別重要的場合。它對於資料量較小的數列重複排序是非常好的。

由於開始時每組只有很少整數,所以排序很快。之後每組含有的整數越來越多,但是由於這些數也越來越有序,所以排序速度也很快。

然而,情況並不總是這麼理想的,在一些特定(但並不算罕見)的情況下,雖然經過了很多趟排序但是資料卻沒有變得更有序。例如,如果用上面的演算法對下面這些數進行排序

1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15, 8, 16

會得到以下結果:

after gap(8): 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15, 8, 16
after gap(4): 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15, 8, 16
after gap(2): 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15, 8, 16
after gap(1): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16

在 gap=1 之前的每一趟排序都在浪費時間!

這種壞情形是可以避免的,就是把上面的增量數列(1, 2, 4, 8)改成Hibbard增量(1, 3, 5, 7)。

由此可見,增量數列的選擇對希爾排序的效能有著極大的影響。[Mark Allen Weiss]指出,最好的增量序列是 Sedgewick提出的 (1, 5, 19, 41, 109,...),該序列的項來自 9 * 4^i - 9 * 2^i + 1 和 4^i - 3 * 2^i + 1 這兩個算式。