詳解希爾排序

 

希爾排序
   基本思想:希爾排序把n個元素按一定的間隔分成幾組,然後按組爲單位進行插入排序。 。

   將待排記錄序列以一定的增量間隔h 分割成多個子序列,對每個子序列分別進行一趟直接插入排序, 然後逐步減小分組的步長h ,對於每一個步長h 下的各個子序列進行同樣方法的排序,直到步長爲1 時再進行一次整體插入排序。

  因爲不管記錄序列多麼龐大,關鍵字多麼混亂,在先前較大的分組步長h下每個子序列的規模都不大,用直接插入排序效率都較高。 儘管在隨後的步長h遞減分組中子序列越來越大,但由於整個序列的有序性也越來越明顯,則排序效率依然較高。這種改進抓住了直接插入排序的兩點本質,大大提高了它的時間效率。

希爾算法的本質是縮小增量排序,是對直接插入排序算法的改進。
  
程序的增量序列常採用的表達式:
(1)h=3*h+1。(h=1,4,13,40,121,364...)
(2)h=2^k-1。(h=1,3,7,15,31,63...

例:待排序列:5,9,1,4,8,2,6,3,7

package cn.test;


import java.util.Arrays;   
public class SortTest{  
 /**  
     * 以指定的步長將數組元素後移,步長指定每個元素間的間隔  
     * @param array 待排序數組  
     * @param startIndex 從哪裏開始移  
     * @param endIndex 到哪個元素止  
     * @param step 步長  
     */  
    private void move(int[] array, int startIndex, int endIndex, int step) {   
        for (int i = endIndex; i >= startIndex; i -= step) {   
            array[i + step] = array[i];   
        }   
    }   
    
   /**  
     * 希爾排序算法的實現,對數組中指定的元素進行排序  
     * @param array 待排序的數組  
     */  
    public void shelltSort(int[] array) {   //升序排列
        //初始步長,實質爲每輪的分組數   
        int step = initStep(array.length);   
  
        //第一層循環是對排序輪次進行循環。(step + 1) / 2 - 1 爲下一輪步長值   
        for (; step >= 1; step = (step + 1) / 2 - 1) {   
            //對每輪裏的每個分組進行循環   
            for (int groupIndex = 0; groupIndex < step; groupIndex++) {   
                //對每組進行直接插入排序   
                insertSort(array, groupIndex, step);   
            }   
        }   
    }   
  /**  
     * 直接插入排序實現  
     * @param array 待排序數組  
     * @param groupIndex 對每輪的哪一組進行排序  
     * @param step 步長  
     * @param end 整個數組要排哪個元素止  
     * @param c 比較器  
     */  
    private void insertSort(int[] array, int groupIndex, int step) {   
        int startIndex = groupIndex;//從哪裏開始排序   
        int endIndex = startIndex;//排到哪裏   
        /*  
         * 排到哪裏需要計算得到,從開始排序元素開始,以step步長,可求得下元素是否在數組範圍內,  
         * 如果在數組範圍內,則繼續循環,直到索引超現數組範圍  
         */  
       while ((endIndex + step) <= array.length-1) {   
            endIndex += step;   
        }   
        // i爲每小組裏的第二個元素開始   
        for (int i = groupIndex + step; i <= endIndex; i += step) {   
            for (int j = groupIndex; j < i; j += step) {   
                int insertedElem = array[i];   
                //從有序數組中最一個元素開始查找第一個大於待插入的元素   
                if (array[j]>insertedElem) {   
                    //找到插入點後,從插入點開始向後所有元素後移step位   
                    move(array, j, i - step, step);   
                    array[j] =insertedElem;   
                    break;   
                }   
            }   
        }   
    }
    private static int initStep(int len) {  
    //從最小步長1推導出最長初始步長值
        /*  
         * 初始值設置爲步長公式中的最小步長,從最小步長推導出最長初始步長值,即按照以下公式來推:  
         * 1,3,7,15,...,2^(k-1)-1,2^k-1  
         * 如果數組長度小於等於4時,步長爲1,即長度小於等於4的數組不用分組,此時直接退化爲直接插  
         * 入排序  
         */  
        int step = 1;   
        //試探下一個步長是否滿足條件,如果滿足條件,則步長置爲下一步長   
        while ((step + 1) * 2 - 1 < len - 1) {   
            step = (step + 1) * 2 - 1;   
        }   
        System.out.println("初始步長 : " + step);   
        return step;   
    }   
      /**  
       * 測試  
     * @param args  
     */  
    public static void main(String[] args) {   
        int[] intArr = { 5, 9, 1, 4, 1, 2, 6, 3, 8, 0, 7 };  
        System.out.println("源 : " + Arrays.toString(intArr));    
        SortTest test=new SortTest();   
       
       test.shelltSort(intArr);
       System.out.println("升 : " + Arrays.toString(intArr));   
     }
    }   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章