網址: https://www.cnblogs.com/dreamer123/p/9518430.html
- 直接插入排序:
- 基本操作是將一個記錄插入到已排好序的有序表中,從而得到一個新的、記錄數增1的有序表。
- 例如 2 1 5 3 4 第一次,將前1個元素看成一個有序表【2】,然后將1插入這個有序表,得到一個新的有序表【1 2】;再降5插入這個新的有序表,得到【1,2,5】;以此類推,最后得到【1,2,3,4,5】。
- 代碼:
void InsertSort(int a[]) { cout<<"before:"; for(int i=0; i<N; i++) cout<<" "<<a[i]; cout<<endl; for(int i=1; i<N; i++) { if(a[i]<a[i-1]) // 需要將[i]插入【前面大小為i的】有序子表 { int temp=a[i]; int j=0; for(j=i-1; j>=0 && (temp<a[j]); j--) //此處j>=0 確保不會下標溢出a的“管轄區域” a[j+1]=a[j]; a[j+1]=temp; } } cout<<"after:"; for(int i=0; i<N; i++) cout<<" "<<a[i]; cout<<endl; }
- 效果:
- 復雜度:O(N2) 是否穩定:是
2. 折半插入排序
- 確定記錄在“當前”有序表中的插入位置時,采用折半查找;(與直接插入排序相比,只是減少了關鍵字之間的比較次數,並沒有改變記錄的移動次數)
- 代碼:
void BInsertSort(int a[]) { cout<<"Call BInsertSort fun!"<<endl; cout<<"before:"; for(int i=0; i<N; i++) cout<<" "<<a[i]; cout<<endl; for(int i=1; i<N ; i++) { int temp=a[i]; int low=0; int high=i-1; while(low<=high) { int m=(low+high)/2; if(a[m]<=temp) low=m+1; else high=m-1; } for(int j=i-1; j>=high+1; j--) { a[j+1]=a[j]; } a[high+1]=temp; } cout<<"after:"; for(int i=0; i<N; i++) cout<<" "<<a[i]; cout<<endl; }
- 效果:
- 時間復雜度:O(N2) 穩定性:是
- 注意:(a[m]<=temp)語句去掉等於號,則可能不具有穩定性; 例如 【2,5a,6,5b】 當5b插入有序表【2,5a,6】時,low=0,high=2,m=1; 此時執行else語句:high=0; then, low=0,high=0,m=0, low=1。 最后移動【2,5a,5a,6】,然后a[high+1]=temp;得到【2,5b,5a,6】,兩個5的相對順序被打亂。 Solution:若if語句中的條件循環改為(a[m]<temp),則后面的high變量用low代替。
3. 希爾排序,又稱縮小增量排序。
- 對於直接插入排序,當待排序記錄為“正序”時,其時間復雜度可提高為O(N)。故,當待排序序列按關鍵字“基本有序”時,直接插入排序的效率就可以大大提高。另一方面,由於直接插入排序算法簡單,咋在n值很小時效率也比較高。根據以上兩點出發,對直接插入排序進行改進得到希爾排序。
- 先將整個待排序記錄序列分割成若干個子序列分別進行直接插入排序,待整個序列中的記錄基本有序時,再對全體記錄進行一次直接插入排序。
- 分成的子序列不是“連續”的,而是相差一個增量d。如對於序列【a1, a2, a3, a4, a5, a6, a7, a8, a9, a10】。 第一趟,d=5: 得到子序列【a1, a6 】,【a2, a7 】,【a3, a8,】,【a4, a9,】,【 a5, a10】,對以上子序列進行直接插入排序; 第二趟d=3: 得到子序列【a1, a4, a7,a10】,【 a2, a5, a8, 】,【a3, a6, a9】對這兩個子序列分別進行直接插入排序;第三趟d=1,對整個序列進行直接插入排序。
- 時間復雜度:是所取“增量”序列的函數; 增量序列的取法注意:應使增量序列的值沒有除1以外的公因子,並且最后一個增量必須等於1。
- 穩定性:否 如【3,1a,1b,4】 第一趟d=2:得到【1b,1a,3,4】;d=1,序列不變。最后的排序結果為【1b,1a,3,4】,兩個1的相對順序發生了變化。