1. 程式人生 > >排序演算法之 插入排序、希爾(shell)排序 及其時間複雜度和空間複雜度

排序演算法之 插入排序、希爾(shell)排序 及其時間複雜度和空間複雜度

        有一個已經有序的資料序列,要求在這個已經排好的資料序列中插入一個數,但要求插入後此資料序列仍然有序,這個時候就要用到一種新的排序方法——插入排序插入排序的基本操作就是將一個數據插入到已經排好序的有序資料中,從而得到一個新的、個數加一的有序資料,演算法適用於少量資料的排序,時間複雜度為O(n^2)。是穩定的排序方法。插入演算法把要排序的陣列分成兩部分:第一部分包含了這個陣列的所有元素,但將最後一個元素除外(讓陣列多一個空間才有插入的位置),而第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成後,再將這個最後元素插入到已排好序的第一部分中。

        插入排序的基本思想是:每步將一個待排序的紀錄,按其關鍵碼值的大小插入前面已經排序的檔案中適當位置上,直到全部插入完為止。

        希爾排序是一種插入排序演算法,它出自D.L.Shell,因此而得名。Shell排序又稱作縮小增量排序。

演算法分析

         插入排序的思想:         從第二個元素開始往後,依次選擇哨兵元素和前面的元素比較,如果前一個元素大於該哨兵元素(從小到大排序),則把前面那個元素移動到後一個位置;繼續往前比較,直到找某個元素不大於該哨兵元素,則把哨兵元素插入到位置上;    插入排序的步驟: 1、第二個元素開始外後選擇一個哨兵元素; 2、讓哨兵元素和前面的元素進行比較,找到合適的位置插入; 3、迴圈上面兩步,直到選擇完所有元素;   shell排序的思想:        shell排序是相當於把一個數組中的所有元素分成幾部分來排序;先把幾個小部分的元素排序好,讓元素大概有個順序,最後再全面使用插入排序。一般最後一次排序都是和上面的插入排序一樣的;
       shell排序的步驟:        比如: 陣列有10個元素,增量 d = 5;則比較元素為:array[0]    array[0+d]    array[0+2d]  array[0+3d];(當然 d 會改變的,d = d/2  或者 d = d -2) 第一次  d = 5  比較的元素為:array[0]  , array[5]   第二次  d = d/2 = 2 比較元素為:array[0]  , array[2]  , array[4] , array[6] , array[8] 第三次  d = d/2 = 1 比較元素為:從array[0] 到  array[9]         其實上面的插入排序可以看作是 增量 d = 1的shell排序;當然同理,shell排序 最後增量 d = 1時, 就是插入排序了;

程式碼實現

#include<stdio.h>
 #include<stdlib.h>
 
 void print_array(int *array, int length)
 {
     int index = 0;
     printf("array:\n");
     for(; index < length; index++){
         printf(" %d,", *(array+index));
     }   
     printf("\n\n");
 }
 
 void insertSort(int *array, int length)
 {
     int index_i, index_j, tmp;
     
     for (index_i = 1; index_i < length; index_i++){ // 從第二個元素往後得到哨兵元素
         tmp = array[index_i];
         for (index_j = index_i - 1; index_j >= 0 ; index_j--){ // 和前面有序的元素進行比較 調整
             if (array[index_j] > tmp) array[index_j + 1] = array[index_j];
             else break;
         }   
         array[index_j + 1] = tmp;// 找到合適的位置插入
     }   
 }
 
 void shellSort(int *array, int length)
 {
     int d = 5;
     int i , j, tmp;
     while(d){ // 判斷增量
         for ( i = d; i < length; i = i + d){ // 從第一個增量值往後  得到哨兵元素 
             tmp = array[i];
             for (j = i -d ; j >= 0; j = j - d){ // 和前面有序的元素進行調整
                 if (array[j] > tmp) array[j+d] = array[j];
                 else break;
             }
             array[j+d] = tmp;
         }
         d = d/2; // 縮小增量的值
     }
 }
 
 int main(void)
 {
     //int array[] = {12, 1, 32, 201, 9987, 5, 10, 10090, 123, 453};
     int array[] = {2, 1, 3, 201, 987, 5, 10, 90, 13, 45};
     int length = (sizeof(array)) / (sizeof(array[1]));
     
     print_array(array, length);
     insertSort(array, length);
     print_array(array, length);
     
     printf("\n\n======================\n\n");
     shellSort(array, length);
     print_array(array, length);
 
     return 0;
 }

        執行結果

        

時間複雜度

插入排序的時間複雜度:

在最好的情況下(元素已經排好順序):那麼只需要迴圈 n-1  次就可以了,時間複雜度 O(n)

在最差的情況下 (元素是逆序的):要迴圈調整次數: [ n * (n-1) ] / 2 ,時間複雜度為 O(n ^ 2)

平均時間複雜度為:O(n ^ 2)

shell排序的時間複雜度:   

shell排序的時間複雜度是根據選中的 增量d 有關的,所以分析shell排序的時間複雜度是個比較麻煩的事;這裡只給出答案,不推算了;

在最優的情況下,時間複雜度為:O(n ^ (1.3) )   (元素已經排序好順序)

在最差的情況下,時間複雜度為:O(n ^ 2);

空間複雜度

   空間複雜度都為:O(1);

        若有不正確之處,望大家指正,共同學習!謝謝!!!