1. 程式人生 > >希爾排序原理(java實現)

希爾排序原理(java實現)

  希爾排序也是排序演算法的一種,先說他的定義,希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序演算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個檔案恰被分成一組,演算法便終止。(摘自百度百科)
  
  看不懂,對吧,我用白話文說一說,其實他就是一個改良版的插入排序(插入排序可以參考我以前的部落格),為什麼這麼說呢,如果你仔細去研究插入排序演算法,很快就會發現,這種演算法的效率與初始資料的狀態有關,假如初始資料本來就是有序的,那麼,排序將相當快,初始資料基本上有序時,簡單的插入排序也有很高的效率,希爾排序,就是基於這個事實,設計出來的更高效的演算法,他是這樣的原理,首先定義一個步長(也就是增量),通過這個步長將原始序列劃分成多個子序列,並將這些子序列進行簡單插入排序,然後再選一個較小的步長,再將這個原始序列劃分成多個子序列,再針對這些子序列進行簡單插入排序,直到最後,步長不能再小了,也就是步長為1了,再進行簡單插入排序之後,整個序列就變成有序序列了
  
  其實,步長為1的時候進行簡單插入排序,就是真正的簡單插入排序,只不過,像上面說的,這個資料的初始狀態已經是基本上是有序了,所以就說,希爾排序是簡單插入的改良版
  
  如果你看到這裡,還是不懂的話,沒關係,來舉個例子,假定現在有這樣一個整型陣列{1,5,2,7,3,4,6,9,8,10},首先,選定一個步長,這個步長的選定是很有講究的,不過這裡我們暫且不說這個,後面我會提,就比如現在我們選定步長為3,然後,怎麼就能將原始序列劃分成多個數列了呢?是這樣的,先把第1個數拿出來,然後,1+3,就是第4個數,1+2x3,就是第7個數,1+3x3就是第10個數,1+4x3,就是第13個數,但是,出界了,那麼就不往後看了,所以,這個子序列就是{1,7,6,10},然後再拿出第2個數,2+3,就是第5個數,2+2x3就是第8個數,2+3x3就是第11個數,但是第11個數不存在,那麼第二個子序列就是{5,3,9},再拿出第3個數,3+3,第6個數,3+2x3,第9個數,3+3x3,第12個數,出界,那麼這個子序列就是{2,4,8},這樣我們就選擇出了這四個子序列,分別對這四個子序列進行簡單插入排序,然後這四個序列就分別都是有序的了,原序列就變成了{1,3,2,6,5,4,7,9,8,10},讀者可以自己拿張紙,自己排一下,就明白了,然後再將步數變成2,經歷上面的同樣的過程,最後再將步數變成1,再經歷上面同樣的過程,那麼,整個序列就有序了,讀者可以看著下面的程式碼,自己走一走步驟,就明白了
  
  下面是Java程式碼實現
  
  

class Demo
{
    public static void main(String[] args)
    {
        //定義整型陣列
        int[] arr = {1,5,2,7,3,4,6,9,8,10};
        //呼叫希爾排序函式
        Shell(arr);
        //輸出排序後的陣列
        for(int i=0;i<arr.length;i++)
        {
            System.out.print(arr[i]+"   ");
        }
    }
    //定義希爾排序函式
public static void Shell(int[] arr) { //dk是步長 int dk = arr.length; while(dk!=1) { //剛開始選擇長度的一半作為步長,每次減少一半 dk = dk/2; //k是每個子序列的第一個元素的下標 for(int k=0;k<=dk;k++) { //通過改變i來改變倍數,確定下標 for
(int i=1;k+i*dk<arr.length;i++) { //j是子序列中,小於i的所有下標 for(int j=0;j<i;j++) { //子序列進行插入排序 if(arr[k+j*dk]>arr[k+i*dk]) { int tmp = arr[k+i*dk]; for(int p=i;p>j;p--) arr[k+p*dk] = arr[k+(p-1)*dk]; arr[k+j*dk] = tmp; } } } } } } }

  不太好理解,慢慢來,然後在這裡說一下關於步長的選擇問題,那是數學家研究的問題,有興趣的讀者可以去相關網站研究