直接插入排序
定义:直接插入排序是一种最简单的排序方式,因此也被称为简单插入排序;
基本思想:第i趟排序将序列中的第i+1个元素ki+1插入到一个已经按有序的子序列(k1,k2,···,ki)中的合适的位置,使得插入后的序列仍然保持按值有序。
有这样一个数据元素序列{3,6,4,2,11,10,6’},其中6’表示该元素与本序列中的其他元素有重复,在此加以区分。
①首先该序列中已存在一个有序的子序列。
{(3,6),4,2,11,10,6’}
②接下来要将4插入到这个子序列中,得到一个含有3个元素的有序的子序列。首先应当判断4应当插入的位置,然后才能进行插入。具体方法是,从元素6开始向左查找。因为4小于6,因此应当将元素6向后移动;因为元素4大于元素3,因此将4插入到3和6之间。这样在原序列中得到一个按值有序的子序列。
{(3,4,6),2,11,10,6’}
上述这个插入过程成为一趟直接插入排序。
按照这种插入的方法,可将后续4个元素逐一插入到前面的子序列中,形成新的子序列。当子序列与原序列长度一样时,插入排序结束。
在进行第1趟的插入排序时,可将第1个元素看成长度1,按值有序的子序列,然后将第2个元素插入到这个子序列中。以此类推,第i趟排序将序列中的第i+1个元素ki+1插入到一个已经按值有序的子序列(k1,k2,···,ki)中的合适的位置,使得插入后的序列仍然保持按值有序。
上述元素序列{3,6,4,2,11,10,6’}的直接插入排序过程如下图所示:
开始状态:{(3),6,4,2,11,10,6’}
↓
第1趟排序:{(3,6),4,2,11,10,6’}
↓
第2趟排序:{(3,4,6),2,11,10,6’}
↓
第3趟排序:{(2,3,4,6).11.10.6’}
↓
第4趟排序:{(2,3,4,6,11),10,6’}
↓
第5趟排序:{(2,3,4,6,10,11),6’}
↓
第6趟排序:{(2,3,4,6,6’,10,11)}
从这个插入排序的过程中可以看出:一个包含有n个元素的序列,需要n-1趟的直接插入排序就可以将原序列排列有序。直接插入排序的算法描述如下:
void sort(keytype k[],int n)
{
int i,j;
keytype tmp;
for(i=2;i<=n;i++)
{
tmp=k[i];//将k[i]保存在临时变量tmp中
j=i-1;
while(j>0&&tmp)//找到tmp的插入位置
k[j+1]=k[j--];//将k[j]后移,再将j减1
k[j+1]=tmp;//将元素tmp插入到指定位置,第i-1趟插入完成
}
}
通过上面的算法可以看出,将元素k[i]=tmp插入到子序列k[1]~k[i-1]中时要进行一系列的比较,将比k[i]大的元素向后移动,直到找到比k[i]小的第一个元素为止,将k[i]=tmp插入到这个元素的前面。因此本算法的功能是将序列从小到大递增排列。也可模仿该算法设计出从大到小的排列算法。
在本算法中,数据序列用一个keytype类型的数组k[]存放,第一个元素k[0]没有使用。因此在算法中可以省却掉临时变量tmp,用k[0]存储每次待插入的元素k[i],这样就节省了系统的空间开销。因为keytype类型的大小是不一定的。
【实例】
编写一个C程序,实现数据序列{2,5,6,3,7,8,0,9,12,1}的直接插入排序,要求从大到小,并输出排序后的数列元素。
【分析】
该数据序列包含10个元素,因此可以将它放到一个含有11个单元的数组中,第0号元素作为存放每次待插入的元素k[i]的空间,在这里不能直接照搬前面给出的算法,要将前面实现从小到大排列的算法加以修改,从而达到从大到小排列的目的。
#include<stdio.h>
sort(int a[],int n)//直接插入排序
{
int i,j;
for(i=2;i<=n;i++)
{
a[0]=a[i];
j=i-1;
while(j>0&&a[0]>a[j])
//改变判断条件,实现从大到小的排列
a[j+1]=a[j--];
a[j+1]=a[0];//将元素a[0]插入到指定位置
}
}
main()
{
int i,a[11]={-111,2,5,6,3,7,8,0,9,12,1};
//初始化序列,a[0]可任意赋值
printf("原序列为:\n");
for(i=1;i<=10;i++)//显示原序列中的元素
printf("%d ",a[i]);
sort(a,10);//插入排序
printf("\n排序后的序列为:\n");
for(i=1;i<=10;i++)//输出排序后的结果
printf("%d ",a[i]);
return 0;
}
运行结果为: