插入排序:将待排序元素分为已排序子集和未排序子集,一次从未排序子集中的一个元素插入已排序子集中,使已排序自己仍然有序;重复执行以上过程,指导所有元素都有序为止。
希尔排序又称为缩小增量排序,它也是一种属插入排序类的方法,但在事件效率上较前述集中排序方法都有较大的改进。
从对直接插入排序的分析得知,其算法时间复杂度为
O(n2)
,但是若待排记录序列为“正序”时,其时间复杂度可提高至
O(n)
。由此可设想,若待排记录序列按关键字基本有序,即序列中具有下列特性
L.data[i].key<max1≤j<i{L.data.key}
的记录较少时,直接插入排序的效率就可大大提高,从另一方面来看,由于直接插入排序的算法简单,则在n值很小时效率也比较高。希尔排序正是从这两点分析出发对直接插入排序进行改进得到的一种排序方法。
基本算法思想:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。
从上述排序过程可见,希尔排序的一个特点是:子序列的构成不是简单地逐段分割,而是将相隔某个增量的记录组成一个子序列。如上图中,第一趟排序时的增量为5,第二趟排序时的增量为3,由于在前两趟的插入排序中记录的关键字是和同一子序列中的前一个记录的关键字进行比较,因此关键字较小的记录就不是一步一步地往前挪动,而是跳跃式地往前移,从而使得在进行最后一趟增量为1的插入排序时,序列已基本有序,只要作记录的少量比较和移动即可完成排序,因此希尔排序的时间复杂度较直接插入排序低。
- 类型定义
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 100
typedef int KeyType;
typedef struct /*数据元素类型定义*/
{
KeyType key;/*关键字*/
}DataType;
typedef struct /*顺序表类型定义*/
{
DataType data[MaxSize];
int length;
}SqList;
- 希尔排序函数
void ShellInsert(SqList *L,int c)
/*对顺序表L进行一次希尔排序,c是增量*/
{
int i,j;
DataType t;
for(i=c+1;i<=L->length;i++) /*将距离为c的元素作为一个子序列进行排序*/
{
if(L->data[i].key<L->data[i-c].key) /*如果后者小于前者,则需要移动元素*/
{
t=L->data[i];
for(j=i-c;j>0&&t.key<L->data[j].key;j=j-c)
L->data[j+c]=L->data[j];
L->data[j+c]=t; /*依次将元素插入到正确的位置*/
}
}
}
void ShellInsertSort(SqList *L,int delta[],int m)
/*希尔排序,每次调用算法ShellInsert,delta是存放增量的数组*/
{
int i;
for(i=0;i<m;i++) /*进行m次希尔插入排序*/
{
ShellInsert(L,delta[i]);
}
}
- 主函数
void InitSeqList(SqList *L,DataType a[],int n)
/*顺序表的初始化*/
{
int i;
for(i=1;i<=n;i++)
{
L->data[i]=a[i-1];
}
L->length=n;
}
void DispList(SqList L,int n)
/*顺序表的输出*/
{
int i;
for(i=1;i<=n;i++)
printf("%4d",L.data[i].key);
printf("\n");
}
void main()
{
DataType a[]={78,29,45,10,80,21,55,3,60,32};
int delta[]={5,3,1};
int n=10,m=3;
SqList L;
InitSeqList(&L,a,n);
printf("[排序前] ");
DispList(L,n);
ShellInsertSort(&L,delta,m);
printf("[希尔排序结果] ");
DispList(L,n);
}
- 测试结果