常見的五類排序算法圖解和實現(插入類:直接插入排序,折半插入排序,希爾排序)


基本的五類排序算法(插入,選擇,交換,歸並,基數排序)。排序:將數據元素的一個任意序列,重新排列成一個按關鍵字有序的序列。

排序的穩定性:待排序列中有大於等於2個相同的項,且排序前后,相同項的相對位置是否發生了變化(如果變化了就是不穩定的排序,不變化就是穩定的)

內部排序:若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序;(待排序列全部放入內存)

插入累排序:(直接插入,折半插入,希爾排序)

直接插入排序:

先將序列中第 1 個記錄看成是一個有序子序列, 然后從第 2 個記錄開始,逐個進行插入,直至整個序列有序。

通常是先把第一個記錄看成是一個有序的序列,然后從第二個記錄開始依次進行比較

r2處的38比第一個記錄 r1處的49小,那么38插入到49的前面,把插入位置之后的記錄順次的后移一位,同時把帶插入記錄臨時保存在 r0處(復制監視哨)。

然后把38插入到 r1處

下面是整個過程,一共7趟排序過程

特點:在最后一趟排序前,此序列沒有一個記錄到其最終位置。(所有的插入類排序的特點)

 1 //遞增排序
 2 void insertSort(int list[])
 3 {
 4     int j = 0;
 5     int i = 0;
 6     int temp = 0;
 7     //對待排序列直接插入排序
 8     //一般把第一個記錄先看成是有序子序列,然后從第二個記錄開始,逐個的和前面去比較,插入
 9     for (i = 1; i <= 8; i++) {
10         //和前面的有序子序列的記錄,逐個的比較,直接插入
11         if (list[i] < list[i - 1]) {
12             //復制為監視哨
13             temp = list[i];
14             //移位
15             list[i] = list[i - 1];
16             //繼續順次的和前面的記錄進行比較,如果還有不小於它的,那么繼續后移,直接插入排序
17             for (j = i - 1; temp <= list[j]; j--) {
18                 //記錄后移
19                 list[j + 1] = list[j];
20                 //插入
21                 list[j] = temp;
22             }
23         }
24     }
25 }
26 
27 int main(void)
28 {
29     int source[8] = {49, 38, 65, 97, 76, 13, 27, 49};
30     
31     insertSort(source);
32     
33     for (int i = 0; i < 8; i++) {
34         printf(" %d ", source[i]);
35     }
36     
37     return 0;
38 }

13  27  38  49  49  65  76  97 Program ended with exit code: 0

算法評價

最好的情況:待排序記錄按關鍵字從小到大排列(正序)

比較次數:移動次數:0 

最壞的情況:待排序記錄按關鍵字從大到小排列(逆序) 

比較次數:移動次數: 

一般情況:待排序記錄是隨機的,取平均值。 比較次數和移動次數均約為: n^2/4

時間復雜度:T(n)=O(n²) (最壞情況下),最好的情況是O(n),平均時間復雜度是O(n²)

空間復雜度:S(n)=O(1) ,且直接插入排序是穩定排序

 

折半插入排序

用折半查找方法確定插入位置的排序。思想類似直接插入排序,只不過,要設置 mid=不大於(low+high)/2的最大整數,當插入到 mid 左邊(做半區),則改變 high(mid-1),如果插入到 mid 右邊,則改變 low(mid+1)。

初試序列,同樣是把第一個記錄看成是有序的子序列,然后從第二個記錄開始,依次進行比較

假設只有最后的20這個記錄了

mid=(0+6)/2=3,指向39處,20比mid 的值小,插入到 mid 左邊,改變 high=mid-1

重新計算 mid=1,指向13處,繼續和20比較,20比 mid的值大,插入到 mid 右邊,改變 low=mid+1

計算 mid=2,指向30,20比 mid 的值30小,插入到 mid 左邊,改變 high=mid-1

直到low>high時,由折半查找得到的插入位置為low或high+1。

//遞增排序
void insertHalfSort(int list[])
{
    int j = 0;
    int i = 0;
    int low = 0;
    int high = 0;
    int mid = 0;
    int temp = 0;//臨時監視哨
    //一般把第一個記錄先看成是有序子序列,然后從第二個記錄開始,前面去比較,折半插入
    for (i = 1; i <= 8; i++) {
        //復制為監視哨
        temp = list[i];
        low = 0;
        high = i - 1;
        //當 low>high,則找到了插入位置,為 low或者 high+1
        while (low <= high) {
            //循環計算 mid 的值
            mid = (int)((low + high) / 2);
            //待插入記錄和 mid進行比較
            if (temp < list[mid]) {
                //改變 high
                high = mid - 1;
            }else{
                //改變 low
                low = mid + 1;
            }
        }
        //循環結束,說明找到了插入位置,進行移位
        for (j = i - 1; j >= low; j--) {
            list[j + 1] = list[j];
        }
        //插入
        list[low] = temp;
    }
}

折半插入,適用於記錄較多的場景,但是記錄的移動次數和直接插入排序一樣,故時間復雜度一樣。

最好是 o(n),最差是 O(n²),平均是 O(n²)。空間復雜度是 o(1)。

特點:折半插入排序的比較次數和初始的序列無關,因為折半的次數一定,折一次比較一次。和直接插入比較,僅減少了比較次數,移動次數不變。折半插入排序是穩定排序

 

希爾排序(縮小增量排序)

基本思想:對待排序列先作“宏觀”調整,再作“微觀”調整。先取一個正整數 d1 < n,把所有相隔 d1 的記錄放在一組內,組內進行直接插入排序;然后取 d2 < d1,重復上述分組和排序操作,直至 di = 1,即所有記錄放進一個組中排序為止。其中 di 稱為增量。且增量 d逐漸變小。

增量d 的取法:最后一個值一定為1,且其余的值沒有除1之外的公因子。

如圖,增量為5,把相隔5的記錄放到一組內,組內進行直接插入排序

分別把增量為5的記錄找出,放在一組內,每組內直接插入排序

分別對子序列進行插入排序

縮小增量d=3

分別把增量 為3的記錄找出

找完之后,縮小增量d=1,進行插入排序

直接插入排序適用於基本有序的情況,而希爾排序會使整個序列更加的有序。

特點:希爾排序是不穩定的排序方法

 

增量序列取法;

如何選擇增量序列以產生最好的排序效果,至今仍沒有從數學上得到解決。

     1)、沒有除 1 以外的公因子;

     2)、最后一個增量值必須為 1。

記住:分組不是簡單的“逐段分割”,而是將相隔某個增量的記錄組成一個子序列。

 

希爾排序可提高排序速度

     1)、記錄跳躍式前移,在進行最后一趟排序時,已基本有序。 

     2)、分組后 n 值減小,n^2 更小,而 T(n)=O(n^2),所以 T(n) 從總體上看是減小了。時間復雜度為 O(n log2(n)),控件復雜度為 o(1)

 

歡迎關注

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 


免責聲明!

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



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