网址: 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的相对顺序发生了变化。