插入排序的主要操作是插入
其基本思想是: 每次将一个待排序的记录按其关键码的大小插入到一个已经排好序的有序序列中,直到全部记录排好序为止。
插入类排序方法有以下两种: 1、直接插入排序 2、希尔排序
直接插入排序
基本思想:在插入第 i(i>1)个记录时,前面的 i-1个记录已经排好序。
需解决的关键问题?
(1)如何构造初始的有序序列?
(2)如何查找待插入记录的插入位置?
直接插入排序过程示例
关键问题(1)如何构造初始的有序序列?
解决方法: 将第1个记录看成是初始有序表,然后从第2个记录起依次插入到这个有序表中,直到将第n个记录插入。
算法描述:
for (i=2; i<=n; i++)
{
插入第i个记录,即第i趟直接插入排序;
}
关键问题(2)如何查找待插入记录的插入位置?
解决方法: 在i-1个记录的有序区r[1] ~ r[i-1]中插入记录r[i],首先顺序查找r[i]的正确插入位置,然后将r[i]插入到相应位置。
算法描述:
r[0]=r[i];
j=i-1;
while (r[0]<r[j])
{
r[j+1]=r[j];
j--;
}
r[0]有两个作用:
1、进入循环之前暂存了r[i]的值,使得不致于因记录的后移而丢失r[i]的内容;
2、在查找插入位置的循环中充当哨兵。
直接插入排序算法
void insertSort (int r[ ], int n){
for (i=2; i<=n; i++) {
r[0]=r[i]; j=i-1;
while (r[0]<r[j]) {
r[j+1]=r[j];
j=j-1;
}
r[j+1]=r[0];
}
}
排序中的主要操作:移动,比较
直接插入排序算法性能分析
最好情况下(正序):时间复杂度为O(n),
最坏情况下(逆序或反序):时间复杂度为O(n2),
平均情况下(随机排列):时间复杂度为O(n2)。
空间性能:需要一个记录的辅助空间。
直接插入排序算法是一种稳定的排序算法。
直接插入排序算法简单、容易实现,适用于待排序记录基本有序或待排序记录个数较小的情况。
当待排序的记录个数较多时,大量的比较和移动操作使直接插入排序算法的效率降低。
希尔排序
改进的依据:
(1)若待排序记录按关键码基本有序时,直接插入排序的效率可以大大提高;
(2)由于直接插入排序算法简单,则在待排序记录数量n较小时效率也很高。
基本思想: 将整个待排序记录分割成若干个子序列, 在子序列内分别进行直接插入排序, 待整个序列中的记录基本有序时,对全体记录进行直接插入排序。
需解决的关键问题?
(1)应如何分割待排序记录,才能保证整个序列逐步向基本有序发展? (2)子序列内如何进行直接插入排序?
分割待排序记录的目的?
1、减少待排序记录个数;
2、使整个序列向基本有序发展。
如何分割?
子序列的构成不能是简单地“逐段分割”,而是将相距某个“增量”的记录组成一个子序列。
希尔插入排序过程示例
关键问题(1)应如何分割待排序记录?
解决方法: 将相隔某个“增量”的记录组成一个子序列。
增量应如何取?
希尔最早提出的方法是d1=n/2,di+1=di/2。
算法描述:
for (d=n/2; d>=1; d=d/2)
{
以d为增量,进行组内直接插入排序;
}
关键问题(2)子序列内如何进行直接插入排序?
解决方法: 在插入记录r[i]时,自r[i-d]起往前跳跃式(跳跃幅度为d)搜索待插入位置,并且r[0]只是暂存单元,不是哨兵。当搜索位置<0,表示插入位置已找到。 在搜索过程中,记录后移也是跳跃d个位置。 在整个序列中,前d个记录分别是d个子序列中的第一个记录,所以从第d+1个记录开始进行插入。
算法描述:
for (i=d+1; i<=n; i++) //将r[i]插入到所属的子序列中
{
r[0]=r[i]; //暂存待插入记录
j=i-d;
while (j>0 && r[0]<r[j])
{
r[j+d]=r[j]; //记录后移d个位置
j=j-d; //比较同一子序列的前一个记录
}
r[j+d]=r[0];
}
希尔排序
void Shellsort(int r[],int n){
for (d=n/2; d>=1; d=d/2){
for (i=d+1; i<=n; i++){
r[0]=r[i];
j=i-d;
while (j>0 && r[0]<r[j])
{
r[j+d]=r[j];
j=j-d;
}
r[j+d]=r[0];
}
}
}
希尔排序算法的时间性能
希尔排序算法的时间性能是所取增量的函数,而到目前为止尚未有人求得一种最好的增量序列。 研究表明,希尔排序的时间性能在O(n2)和O(nlog2n)之间。当n在某个特定范围内,希尔排序所需的比较次数和记录的移动次数约为O(n1.3 ) 。