排序演算法之一--直接插入排序
直接插入排序中加入了附加記錄,又稱監視哨或者哨兵。
哨兵的主要作用:
① 進人查詢(插入位置)迴圈之前,它儲存了R[i]的副本,使不致於因記錄後移而丟失R[i]的內容;
② 它的主要作用是:在查詢迴圈中監視下標變數j是否越界。一旦越界(即j=0),因為R[0].可以和自己比較,迴圈判定條件不成立使得查詢迴圈結束,從而避免了在該迴圈內的每一次均要檢測j是否越界(即省略了迴圈判定條件j>=1)。
下面的c函式,是基礎的直接插入排序,但是更容易幫助理解哨兵的意思
void insert_sort(int a[],int n)
//待排序元素用一個數組a表示,陣列有
{
int i,j;
int temp;
for ( i=1; i<n; i++) //i表示插入次數,共進行n-1次插入
{
temp=a[i]; //把待排序元素賦給temp,temp在while迴圈中並不改變,這樣方便比較,並且它是要插入的元素
j=i-1;
//while迴圈的作用是將比當前元素大的元素都往後移動一個位置
while ((j>=0)&& (temp<a[j])){
a[j+1]=a[j];
j--; // 順序比較和移動
}
a[j+1]=temp;//元素後移後要插入的位置就空出了,找到該位置插入
}
}
上面的temp就相當於哨兵的意思
下面是改進版的演算法:
void lnsertSort(SeqList R)
{ //對順序表R中的記錄R[1..n]按遞增序進行插入排序
int i,j;
for(i=2;i<=n;i++) //依次插入R[2],…,R[n]
if(R[i].key<R[i-1].key){ //若R[i].key大於等於有序區中所有的keys,則R[i]
//應在原有位置上
R[0]=R[i];j=i-1; //R[0]是哨兵,且是R[i]的副本
do{ //從右向左在有序區R[1..i-1]中查詢R[i]的插入位置
R[j+1]=R[j]; //將關鍵字大於R[i].key的記錄後移
j-- ;
}while(R[0].key<R[j].key); //當R[i].key≥R[j].key時終止
R[j+1]=R[0]; //R[i]插入到正確的位置上
}//endif
}
演算法用c語言實現了,現在來分析一下時間複雜度和空間複雜度
從空間上:
只需要一個哨兵來輔助,因此空間複雜度為O(1)
從時間上:
最好的情況:所有的序列本來就是是需要的序列(順序或者逆序),但還是需要遍歷一遍所有元素,那個器時間複雜度為O(n)
最壞的情況:所有的序列和需要的序列恰恰相反,需要(1,2,3,4,5),原序列是(5,4,3,2,1),那就需要在遍歷每一個元素的時候,要進行一波與前面元素的交換,此時的時間複雜度為2+3+4+...+n 其時間複雜度為O(n2)
一般情況:排序記錄是隨機的,
穩定性:穩定的