資料結構之直接插入排序和希爾排序
資料結構中有很多排序演算法,以下講解直接插入排序和希爾排序,剩下的排序演算法以後會更新。
1, 直接插入排序
(1)直接插入排序概念:從陣列頭一個一個元素開始排序,此時第i個元素之前都是從小到大排序好的,將第i個元素插入到前i個元素中去就好,以此類推,直到將n個元素全部插入完成。直接插入排序相關演算法如下
1 void InsertSort(int A[],int n) 2 { 3 int i,j,temp; 4 for(i=1;i<n;i++) //將各個元素插入到已排好序的序列中 5 { 6 if(A[i]<A[i-1]) //若A[j]元素小於前驅 7 { 8 temp=A[i]; //用temp儲存A[i] 9 for(j=i-1;j>=0&&A[j]>temp;j--) //檢查所有已排好序的元素 10 A[j+1]=A[j]; //大於temp的元素都向後挪位 11 A[j+1]=temp;//將要插入的元素插入到序列中 12 } 13 }14 } 15
2,分析演算法效率
(1) 空間效率:僅使用了常數個輔助單元,因而空間複雜度為O(1);
(2)時間效率:在最好的情況下,表中元素已經有序,此時每插入一個元素,都只需要比較一次而不用移動元素,因而時間複雜度為O(n).
在最壞情況下:表中元素順序剛好與排序結果中的元素順序相反(逆序)總的比較次數為:(n+2)(n-1)/2, 總的移動次數為:∑i=2n(i+1)。
因此,直接插入排序演算法的時間複雜度為O(n2)。
(3)穩定性:直接插入排序是一個穩定的排序演算法。
2,折半插入排序
(1)折半插入排序是直接插入排序的改進型,由於是順序儲存的線性表,所以查詢有序子表時可以用折半查詢實現。
相關程式碼如下
1 void InsertSort(int A[],int n) 2 { 3 int i,j,low,high,mid; 4 for(i=2;i<=n;i++) //依次將A[2]到A[n]插入到前面已排序的陣列中 5 { 6 A[0]=A[i]; //將待排序的元素存放到A[0]位置 7 low=1;high=i-1; //設定折半查詢範圍 8 while(low<=high) //折半查詢(預設遞增序列) 9 { 10 mid=(low+high)/2; // 取中間值 11 if(A[mid]>A[0] 12 high=mid-1; //插入位置在左半部分 13 else 14 low=mid+1; //插入位置在右半部分 15 } 16 for(j=i-1;j>=high+1;j--)//依次將大於待插入元素的值後移一位 17 A[j+1]=A[j]; 18 A[high+1]=A[0];//將待插入元素插入順序表中 19 } 20 }
(2)演算法分析
從上述演算法中,不難看出折半查詢插入排序減少了比較元素的次數,約為O(nlog2n),該比較次數與待排序表的初始狀態無關,僅取決於表中的元素n。
因此,折半查詢排序的時間複雜度為O(n2)。
(3)穩定性:折半插入排序是一種穩定的排序方法。
3,希爾排序
1,希爾排序的思想:先取一個間隔d,分別取A[i],A[i+d],A[i+2d],依次往下取,分成d個子表,依次對子表進行直接插入排序,改變d的值,將d的值減小,然後按照剛才的方法進行排序,當整個表中的元素呈現”基本有序“時,再對全體記錄進行一次直接插入排序。
程式碼如下
1 void ShellSort(int A[],int n) 2 { 3 for(d=n/2;d>=1;d=d/2) //d的變化範圍 4 { 5 for(i=d+1;i<=n;++i) 6 { 7 if(A[i]<A[i-d] //需將A[i]插入有序遞增子表 8 { 9 A[0]=A[i]; //將待插入元素暫存到A[0] 10 for(j=i-d;j>0&&A[0]<A[j];j-=d) 11 A[j+d]=A[j]; // 記錄後移,查詢插入位置 12 A[j+d]=A[0]; //插入 13 } 14 } 15 } 16 }
(2)空間效率:僅使用了常數個輔助單元,因此空間複雜度為O(1);
(3)時間複雜度:當n在某個特定範圍內時,希爾排序的時間複雜度約為O(n1.3).,在最壞的情況下,時間複雜度為O(n2)。
(4)穩定性:不是一種穩定的排序演算法。
(5)適用性:希爾排序演算法僅適用於線性表為順序儲存的情況。