将待排序的记录Ri,插入到已排好序的记录表R1, R2 ,…., Ri-1中,得到一个新的、记录数增加1的有序表。 直到所有的记录都插入完为止。
设待排序的记录顺序存放在数组R[1…n]中,在排序的某一时刻,将记录序列分成两部分:
◆ R[1…i-1]:已排好序的有序部分;
◆ R[i…n]:未排好序的无序部分。
显然,在刚开始排序时,R[1]是已经排好序的。
2 算法实现
void straight_insert_sort(Sqlist *L)
{
int i, j ;
for (i=2; i<=L->length; i++)
{
L->R[0]=L->R[i]; j=i-1; /* 设置哨兵 */
while( LT(L->R[0].key, L->R[j].key) )
{
L->R[j+1]=L->R[j];
j--;
} /* 查找插入位置 */
L->R[j+1]=L->R[0]; /* 插入到相应位置 */
}
}
3 算法说明
算法中的R[0]开始时并不存放任何待排序的记录,引入的作用主要有两个:
① 不需要增加辅助空间: 保存当前待插入的记录R[i],R[i]会因为记录的后移而被占用;
② 保证查找插入位置的内循环总可以在超出循环边界之前找到一个等于当前记录的记录,起“哨兵监视”作用,避免在内循环中每次都要判断j是否越界。
4 算法分析:复杂度为O(n2) 。
折半插入排序
当将待排序的记录R[i] 插入到已排好序的记录子表R[1…i-1]中时,由于R1, R2 ,…, Ri-1已排好序,则查找插入位置可以用“折半查找”实现,则直接插入排序就变成为折半插入排序。
⑴ 算法实现
void Binary_insert_sort(Sqlist *L)
{
int i, j, low, high, mid ;
for (i=2; i<=L->length; i++)
{
L->R[0]=L->R[i]; /* 设置哨兵 */
low=1 ;
high=i-1 ;
while (low<=high)
{
if ( LT(L->R[0].key, L->R[mid].key) )
high=mid-1 ;
else low=mid+1 ;
} /* 查找插入位置 */
for (j=i-1; j>=high+1; j--)
L->R[j+1]=L->R[j];
L->R[high+1]=L->R[0]; /* 插入到相应位置 */
}
}
从时间上比较,折半插入排序仅仅减少了关键字的比较次数,却没有减少记录的移动次数,故时间复杂度仍然为O(n2) 。