插入排序
直接插入排序
直接插入排序(Straight Insertion Sort)是一種最簡單的排序方法,其基本操作是將一條記錄插入到已經排序號的有序表中,從而得到一個新的,記錄數量增1的有序表
排序方法
1、簡單方法
首先在當前有序區R[1..i-1]中查找R[i]的正確插入位置k(1≤k≤i-1);然后將R[k..i-1]中的記錄均后移一個位置,騰出k位置上的空間插入R[i]。
注意:若R[i]的關鍵字大於等於R[1..i-1]中所有記錄的關鍵字,則R[i]就是插入原位置。
2.改進的方法
一種查找比較操作和記錄移動操作交替地進行的方法。具體做法:
將待插入記錄R[i]的關鍵字從右向左依次與有序區中記錄R[j](j=i-1,i-2,…,1)的關鍵字進行比較:
① 若R[j]的關鍵字大於R[i]的關鍵字,則將R[j]后移一個位置;
②若R[j]的關鍵字小於或等於R[i]的關鍵字,則查找過程結束,j+1即為R[i]的插入位置。
關鍵字比R[i]的關鍵字大的記錄均已后移,所以j+1的位置已經騰空,只要將R[i]直接插入此位置即可完成一趟直接插入排序。
哨兵的作用
算法中引進的附加記錄R[0]稱監視哨或哨兵(Sentinel)。
哨兵有兩個作用:
① 進人查找(插入位置)循環之前,它保存了R[i]的副本,使不致於因記錄后移而丟失R[i]的內容;
② 它的主要作用是:在查找循環中"監視"下標變量j是否越界。一旦越界(即j=0),因為R[0].可以和自己比較,循環判定條件不成立使得查找循環結束,從而避免了在該循環內的每一次均要檢測j是否越界(即省略了循環判定條件"j>=1")。
注意:
① 實際上,一切為簡化邊界條件而引入的附加結點(元素)均可稱為哨兵。
【例】單鏈表中的頭結點實際上是一個哨兵
② 引入哨兵后使得測試查找循環條件的時間大約減少了一半,所以對於記錄數較大的文件節約的時間就相當可觀。對於類似於排序這樣使用頻率非常高的算法,要盡可能地減少其運行時間。所以不能把上述算法中的哨兵視為雕蟲小技,而應該深刻理解並掌握這種技巧。
#include <stdio.h> #include <stdlib.h> #define L 8 void pri_sort(int *a, int l) { int i; for (i = 0; i < l; i++) { if (i == l - 1) printf("%d\n", a[i]); else printf("%d ", a[i]); } } int main() { int i; int j; int sortN[L] = { 0,3,4,6,5,1,2,7 }; for (i = 2; i < L; i++) { sortN[0] = sortN[i];//哨兵 j = i - 1; while (sortN[0]<sortN[j]) { sortN[j + 1] = sortN[j]; j--; } sortN[j + 1] = sortN[0]; } pri_sort(sortN,L); system("pause"); }
算法分析
(1)時間復雜度
從時間看來,排序的基本操作為:比較兩個關鍵字的大小和移動記錄。最好的情況是:正序:待排序中記錄按關鍵字非遞減有序排列 比較一次不移動。最壞的情況是:逆序:待排序序列中記錄按關鍵字非遞增有序排列 比較i次(依次同前面的i-1個記錄進行比較,並和哨兵比較1次,移動i+1次(前面的i-1次個記錄一次向后移動,另外開始時將待插入的記錄移動到監視哨中,最后找到插入位置,又從監視哨中移過去。
折半插入排序
直接插入排序采用順序查找法查找當前記錄在已排好序的序列中的插入位置,這個查找操作可利用折半查找來實現,由此進行的插入排序稱之為這般插入排序
算法步驟
1.待排序的記錄存放在數組r[1....n]中,r[1]是一個有序序列。
2.循環n-1次,每次使用折半查找法,查找[i](i=2,3,4,5.。。n)在已排好序的序列r[1,..i-1]中的插入位置,然后將r[i]插入表長為i-1的有序序列r[1...i-1],直到將r[n]插入表長為n-1的有序序列中,最后得到一個表長為n的有序序列。
void BInserSort(SqList &L) { int i,j; for(i=2;i<=L.length;i++) { L.r[0]=L.r[i];//將待插入的記錄暫存到監視哨中 low=l;high=i-1;//置查找區間初值 while(low<=high)//在r[low..high]中折半查找插入的位置 { m=(low+high)/2; //折半 if(L.r[0].key<L.r[m].key) //插入點在前一子表 high=m-1; else //插入點在后一子表 low=m+1; } for(j=i-1;j>=high+1;j--) //記錄后移 L.r[high+1]=L.r[0];//將r[0]即原來的r[i]插入到正確的位置 }
算法分析
(1)時間復雜度
從時間上比較,折半查找比順序查找快,所以就平均性能來說,折半插入排序優於直接插入排序。這般插入排序所需要的關鍵字比較次數與待排序序列的初始排列無關,僅依賴與記錄的個數。不論初始序列情況如何,在插入第i個記錄時,需要經過[log2 i]+1次比較,才能確定它應插入的位置。所以當記錄初始序列為正序或接近正序時。直接插入排序優於這般插入排序執行的關鍵字比較次數要少。
折半插入排序的對象移動次數與直接插入排序相同,依賴於對象的初始排列。
在平均情況下,折半插入排序僅減少了關鍵字間的比較次數,而記錄的移動次數不變。因此折半插入排序的時間復雜度仍為O(n2)。
(2)空間復雜度
折半插入排序所需附加存儲空間和直接排序相同,只需要一個記錄的輔助空間r[0],所以空間復雜度為O(1)。
算法特點
1.穩定排序
2.因為要進行折半查找,所以只能用於順序結構,不能用於鏈式結構。
2適合初始記錄無序,n較大時的情況。