常見的三種插入排序


1、直接插入排序算法

直接插入排序的基本操作是將一個記錄插到已排隊好的有序表中,從而得到一個新的,記錄增1的有序表。

 1 // 直接插入排序.cpp : 定義控制台應用程序的入口點。  2 //  3 #include "stdafx.h"
 4 #include "stdio.h"
 5 //直接插入排序
 6 void InsertSort(int a[], int len);  7  void main(){  8      int i;  9     int a[5] = {5,3,4,6,2}; 10     InsertSort(a,5); 11 
12     for (i = 0; i < 5; i++) 13         printf("%d ", a[i]); 14 } 15 
16 void InsertSort(int a[] ,int len){ 17     int temp,i,j; 18     for (i = 1; i < len;i++) 19  { 20         if (a[i] < a[i - 1]){ 21             temp = a[i];          //用一個臨時變量存一下
22                 for (j = i - 1; a[j] > temp && j>=0; j--){ 23                     a[j + 1] = a[j];                   //凡事比i這個數大的就要后移,因為大的數總是在后面
24  } 25                 a[j+1] = temp;   //這里需要注意的是j+1,調bug好累勒 ->@@
26  } 27  } 28 }

直接插入排序算法分析

根據代碼我們來解釋一下直接插入排序的核心

例如,我們要對5,3,4,6,2這幾個數進行排序

a[] 0 1 2 3 4
5 3 4 6 2

 

 

 

當這個數組進入函數后,下標首先定義到i = 1,即排序前,首先定義為a[0] = 5即是有序的。

進入循環內,比較a[1] 是否小於 a[0] 發現是小於的,這個時候按理說是要把a[0]這個元素右移動1位。然后將a[1]這個元素插在a[0]的位置上

但是考慮到這樣子將覆蓋原來的a[1]的值,所以先將a[1]的值拷貝一份給temp,然后將a[0]右移一位,再將temp的值傳給a[0] .即

a[] 0 1 2 3 4
3 5 4 6 2



 

這時i =2了。此時a[0],a[1]屬於有序的序列了,我們此時再次比較a[2]是否小於a[1](前一位),4<5,滿足if條件

temp = a[2] 先拷貝一份,再將a[1] 右移一位,再次比較a[0]是否大於temp ,發現3並沒有大於4,由此可見只要i前面有序數存在大於a[i]的值,有序序列就要向后移動,

然后再把a[i] 插在正確的位置。

a[] 0 1 2 3 4
3 4 5 6 2

 

 

 

當i = 3時,這個時候6比5大,不滿足if條件,也可以發現,前面已經都是有序序列{3,4,5,6}.

a[] 0 1 2 3 4
3 4 5 6 2

 

 

最后當i = 4時,發現2 < a[3] 這個時候同理前面操作,先將a[4]拷貝一份給temp ,a[4] = a[3],右移一位

再次比較 ,發現temp < a[2] , a[3] =a[2] ,右移一位

再次比較 ,發現temp < a[1] , a[2] =a[1] ,右移一位

再次比較 ,發現temp < a[0] , a[1] =a[0] ,右移一位

此時就可以把temp 賦值給了a[0] ,這個時候就已經排序完成了。

a[] 0 1 2 3 4
2 3 4 5 6

 

 

 

直接插入排序復雜度分析

從空間上看,它只需要一個輔助空間temp ,因此我們關鍵看它的時間復雜度。

當最好的情況下,也就是序列本身就是有序的 ,這個時候我們只有進行每次的if判斷(第20行),比較的次數n-1,移動的次數0,這個時候時間復雜度O(n)

如果排序記錄是隨機的話,那么根據概率相同的情況原則,平均比較和移動的次數約為(n^2)/4 次,因此我們可以得出直接插入排序法的書劍復雜度為O(n^2) 從這里也可以看出

直接插入排序比冒泡排序和簡單選擇排序性能要好一點,是一個穩定的排序算法。

 

2、折半插入排序算法

折半插入排序(Binary Insertion Sort)是對插入排序算法的一種改進,所謂排序算法過程,就是不斷的依次將元素插入前面已排好序的序列中。

基本思想:

基本思想和直接插入排序相同

不同在於查找插入位置

直接插入排序是采用順序查找法,而折半插入排序排序是采用二分查找思想。

 1 #include <stdio.h>
 2 
 3 void BlnInsertSort(int a[], int len);  4 int main(){  5 
 6     int i;  7     int a[5] = { 15, 3, 41, -6, 2 };  8     BlnInsertSort(a, 5);  9 
10     for (i = 0; i < 5; i++) 11         printf("%d ", a[i]); 12 
13     return 0; 14 } 15 
16 void BlnInsertSort(int a[], int len){ 17     int i, temp, j; 18     for (i = 1; i < len;i++) 19  { 20         temp = a[i];   //將要插入的元素拷貝一份 21         int low , high,mid; 22             low = 0, high = i-1; 23             while (low <=high)  //在[l...h] 中尋找插入的位置 24  { 25                 mid = (low + high) / 2; //折半 26                 if (mid <= temp) 27  { 28                     low = mid+1; //插在高半區 29  } 30                 else{ 31                     high = mid-1; //插在低半區 32  } 33  } 34             for (j = i - 1; j >= high + 1;--j) //騰出high+1的位置 35  { 36                 a[j + 1] = a[j]; //記錄后移 37  } 38             a[j + 1] = temp; 39 
40  } 41 }

假如要將i 位置上的數插入前面的有序[0...4]序列中,令 i = 5,h =i-1 ,l = 0, m = (h+l)/2;

 

 發現 i = 5 對應的數 小於 m 對應的數,這個時候 h =m -1 ,查找位置縮小到左半區間 ,m = (h+l)/2;

 

再次比較m 和 i對應的值,發現27 > 13 ,此時發現27應該在m的右半部分。於是 l = m+1 ,m = (h+l)/2;

再次比較m 和 i 對應的值,發現27 < 38 ,此時發現27應該在m的左半部分。於是 h = m-1 ,m = (h+l)/2;

 

此時再次觀察發現h <l這個時候循環就應該結束了。我們i要插入的位置即是 h+1 ,於是將數組元素開始移動,騰出位置,准備插入數據。

這就是二分插入排序的基本思想。

折半插入排序算法復雜度分析

1、折半插入排序要比直接插入排序快,所以折半插入排序的平均性能要優於直接插入排序。

2、折半插入排序的關鍵碼比較次數和待排序序列的初始序列無關,僅依賴於對象的個數,再插入第i個對象時,需要經過{log2 i}次關鍵碼比較,才能確定它插入的位置。

3、當n比較大時,總關鍵碼的比較次數要比直接插入排序情況好的多,但要比最好的情況差。

4、在對象的初始排序已經按關鍵碼排好或接近有序時,直接插入排序比折半插入排序的關鍵碼的比較次數要少。

5、折半插入排序的對象的移動次數與直接插入排序相同,依賴於對象的初始序列,折半插入排序只是在數據較多的情況下減少了比較次數。

3、希爾排序算法

希爾排序(Shell's Sort)是插入排序的一種又稱“縮小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法。
該方法因D.L.Shell於1959年提出而得名。
基本思想:先將整個待排記錄序列分割成若干個子序列,分別進行直接插入排序,待整個序列中的記錄 “基本有序” 時,再對全體記錄進行一次直接插入排序。
希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。【百度百科】
看着晦澀的描述有點不好理解,我們來看看具體是怎么操作的。
 
 

用一個變量dk = 5 ,將全部序列分割成

 

這里要做的事將a[0] ,a[5] ,a[10];a[1] ,a[6] ,a[11] ;a[2] ,a[7] ,a[12];a[3] ,a[8] 這幾組數做直接插入排序,注意此時的間隔為5,而不是之前的1,完成后

 

發現規律 ,只要是間隔5的每一組數都是有序的,如{35,41,81},{17,75,91},{11,15,95}都是有序的,這也導致了全部的序列有序性有所提高。

繼續我們現在另dk = 3 ,即將上面的序列分割成間隔為3的子序列,看顏色區分

同理按照5間隔的規則進行排序,排序完后

這個時候有序性又提高了不少。最后再將所有的序列做最后一次的直接插入排序,dk = 1的排序。

直接插入排序在序列元素少,序列的有序性好的情況下,它的效率是非常高的。

 

這個時候就已經全部排序完成了。

代碼:

 1 #include<stdio.h>
 2 
 3 void ShellSort(int a[], int dk[],int len);  4 void InsertSort(int a[], int dk, int len);  5 
 6 int main(){  7 
 8     int i, j, len;  9     int a[13] = { 81, 94, 11, 96, 12, 35, 17, 95, 28, 58, 41, 75, 15 }; 10     int dk[3] = { 0 }; 11     len = sizeof(a) / 4; 12     int increament = len / 3 + 1;  //increament = 5
13     for (j = increament, i = 0; j >= 1; j -= 2, i++)  //dk[0...i-1]={5,3,1}
14         dk[i] = j; 15 
16  ShellSort(a, dk, len); 17 
18     printf("排完序:\n"); 19     for (i = 0; i < len; i++) 20         printf("%d ", a[i]); 21     printf("\n"); 22 
23     return 0; 24 } 25 
26 void ShellSort(int a[], int dk[],int len){ 27     int i,j=0; 28     //按增量序列dk[0...i-1]對順序表進行希爾排序
29     for (i = 0; i < 3; i++){ 30         printf("第%d趟排序: ",++j ); 31         InsertSort(a, dk[i], len);   //一趟增量為dk[i]的插入排序
32  } 33 } 34 
35 void InsertSort(int a[], int dk,int len){ 36 
37     int i, j, temp; 38     for (i = dk; i < len; i++){ 39         if (a[i] < a[i - dk]){ 40             temp = a[i]; 41             //從有往左開始掃描,尋找有沒有大於temp的數(間隔是dk)
42             for (j = i - dk; j >= 0 && (temp < a[j]); j = j - dk){ 43                 a[j + dk] = a[j]; 44  } 45             a[j + dk] = temp; 46  } 47  } 48     //輸出每一趟排序結果
49     for (i = 0; i < len; i++) 50         printf("%d ", a[i]); 51     printf("\n"); 52 }

 希爾排序的特點:

1、一次移動,移動位置較大,跳躍式接近排序后的最終位置。

2、最后一次只需要進行少量的移動。

3、增量序列必須是遞減的,最后一個必須是1.

4、增量序列應該是互質的。

希爾排序算法的復雜度分析

希爾排序算法的效率和增量序列的取值有關系

那么增量序列是如何取值的,取一些什么值才能達到最高的排序效率呢?

這個問題至今都沒有被解決,是一個數學上的難題。

大量研究表明增量序列為:

 

可以獲得不錯的效率,其時間復雜度為O(n^2/3).要優於直接插入排序的O(n^2) 

需要注意的是,最后一個增量必須是1才可以。由於記錄是跳躍性移動的,所以希爾排序並不是一個穩定的排序算法。

常見的內部排序就這3個,嚴奶奶書里面還提到了2-路插入排序表插入排序。

 如果有些錯的地方大家希望可以指出,我們共同進步 -- 共勉之**

參考資料:《大話數據結構》

https://www.bilibili.com/video/av38482403/?spm_id_from=333.788.videocard.5

https://www.bilibili.com/video/av43523066/?p=2


免責聲明!

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



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