歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

經典(Java版)排序算法的分析及實現之二希爾排序

插入排序—希爾排序

希爾排序是1959 年由D.L.Shell 提出來的,相對直接插入排序有較大的改進。希爾排序的實質就是分組插入排序,該方法又稱縮小增量排序。

基本算法:

先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。因為直接插入排序在元素基本有序的情況下(接近最好情況),效率是很高的,因此希爾排序在時間效率上比前兩種方法有較大提高。步長的選擇是希爾排序的重要部分。只要最終步長為1任何步長序列都可以工作。

算法最開始以一定的步長進行排序,然後會繼續以一定步長進行排序,最終算法以步長為1進行排序。當步長為1時,算法變為插入排序,這就保證了數據一定會被排序。Donald Shell 最初建議步長選擇為\frac{n}{2}並且對步長取半直到步長達到 1。雖然這樣取可以比\mathcal{O}(n^2)類的算法(插入排序)更好,但這樣仍然有減少平均時間和最差時間的余地。

希爾排序示例:n=10的一個數組 58 27 32 93 65 87 58 46 9 65,步長為n/2。

第一次排序 步長為 10/2 = 5

   58  27  32  93  65  87  58  46  9  65
    1A                  1B
        2A                    2B
            3A                    3B
                  4A                    4B
                      5A                  5B

    首先將待排序元素序列分組,以5為步長,(1A,1B),(2A,2B),(3A,3B)等為分組標記,大寫字母表示是該組的第幾個元素,數字相同的表示在同一組,這樣就分成5組,即(58,87),(27,58),(32,46),(93,9),(65,65),然後分別對各分組進行直接插入排序,排序後5組為(58,87),(27,58),(32,46),(9,93),(65,65),分組排序只是變得各個分組內的下表,下同。

    第二次排序 步長為 5/2 = 2

    58  27  32  9  65  87  58  46  93  65

    1A      1B      1C      1D      1E

        2A      2B      2C      2D        2E

    第三次排序 步長為 2/2 = 1

    32  9  58  27  58  46  65  65  93  87

  1A  1B  1C  1D  1E  1F  1G  1H  1I  1J

    第四次排序 步長為 1/2 = 0 得到有序元素序列

    9  27  32  46  58  58  65  65  87  93

    按照希爾排序算法定義

/**
    * 按照希爾排序定義實現
    *
    * @param sortList
    */
    static void shellSort1(Integer[] sortList) {
        int i, j, step;
        int len = sortList.length;
        // 步長除以2
        for (step = len / 2; step > 0; step /= 2)
            /**
            *  分別對每個分組進行直接插入排序
            */
            for (i = 0; i < step; i++)
            {
                for (j = i + step; j < len; j += step)
                    if (sortList[j] < sortList[j - step]) {
                        int temp = sortList[j];
                        int k = j - step;
                        while (k >= 0 && sortList[k] > temp) {
                            sortList[k + step] = sortList[k];
                            k -= step;
                        }
                        sortList[k + step] = temp;
                    }
            }
    }

對上面的代碼進行優化,按照從從第一個步長數據開始,依次對每個元素與自己組內的數據進行直接插入排序

  /**
    * 從第一個步長數據開始,依次對每個元素與自己組內的數據進行直接插入排序
    * @param sortList
    */
    static void shellSort2(Integer[] sortList) {
        int j, step;
        int len = sortList.length;
        for (step = len / 2; step > 0; step /= 2)
            for (j = step; j < len; j++)
                /**
                * 從數組第step個元素開始,然後將每個元素與自己組內的數據進行直接插入排序
                */
                if (sortList[j] < sortList[j - step]) {
                    int temp = sortList[j];
                    int k = j - step;
                    while (k >= 0 && sortList[k] > temp) {
                        sortList[k + step] = sortList[k];
                        k -= step;
                    }
                    sortList[k + step] = temp;
                }
    }

算法復雜度

1、時間復雜度

希爾排序耗時的操作有:比較 + 後移賦值。時間復雜度如下:

1) 最好情況:序列是升序排列,在這種情況下,需要進行的比較操作需(n-1)次。後移賦值操作為0次。即O(n)

2) 最壞情況:O(nlog2n)。

3) 漸進時間復雜度(平均時間復雜度):O(nlog2n)

平均時間復雜度:O(nlog2n),希爾排序在最壞的情況下和平均情況下執行效率相差不是很多, 與此同時快速排序(O(log2n))在最壞的情況下執行的效率會非常差。專家們提倡,幾乎任何排序工作在開始時都可以用希爾排序,若在實際使用中證明它不夠快,再改成快速排序這樣更高級的排序算法。

增量序列的選擇

Shell排序的執行時間依賴於增量序列。

1) 最後一個增量必須為1;

2) 應該盡量避免序列中的值(尤其是相鄰的值)互為倍數的情況。

Shell排序的時間性能優於直接插入排序

1)當文件初態基本有序時直接插入排序所需的比較和移動次數均較少。

2)當n值較小時,n和的差別也較小,即直接插入排序的最好時間復雜度O(n)和最壞時間復雜度0()差別不大。

3)在希爾排序開始時增量較大,分組較多,每組的記錄數目少,故各組內直接插入較快,後來增量di逐漸縮小,分組數逐漸減少,而各組的記錄數目逐漸增多,但由於已經按di-1作為距離排過序,使文件較接近於有序狀態,所以新的一趟排序過程也較快。

2、空間復雜度:O(1)

希爾排序是在原輸入數組上進行後移賦值操作的(稱“就地排序”),所需開辟的輔助空間跟輸入數組規模無關,所以空間復雜度為:O(1)

穩定性

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

附件希爾排序下載

------------------------------------------分割線------------------------------------------

免費下載地址在 http://linux.linuxidc.com/

用戶名與密碼都是www.linuxidc.com

具體下載目錄在 /2014年資料/12月/12日/經典(Java版)排序算法的分析及實現之二希爾排序

下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm

------------------------------------------分割線------------------------------------------

歸並排序的實現 http://www.linuxidc.com/Linux/2014-09/107316.htm

直接插入排序的三種實現 http://www.linuxidc.com/Linux/2014-09/107313.htm

直接選擇排序及交換二個數據的正確實現 http://www.linuxidc.com/Linux/2014-09/107315.htm

排序總結之選擇式排序  http://www.linuxidc.com/Linux/2014-09/106564.htm

算法----選擇排序(select sort)http://www.linuxidc.com/Linux/2014-11/109827.htm

Copyright © Linux教程網 All Rights Reserved