希尔排序又称“缩小增量排序”(Diminishing Incerment Sort),是插入排序的一种,因D.L.Shell于1959年提出而得名。直接插入排序当待排序的记录个数较少且待排序序列的关键字基本有序时,效率较高。希尔排序基于以上两点,从“减少记录个数”和“序列基本有序”两个方面对直接插入排序进行了改进。
[算法思想]希尔排序实质上是采用分组插入的方法。先将整个待排记录序列分割成几组,从而减少参与直接插入排序的数据量,对每组分别进行插入排序,然后增加每组的数据量,重新分组,这样当经过几次分组排序后,整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
希尔对记录的分组,不是简单地“逐段分割”,而是将相隔某个“增量”的记录分成一组。1)第一趟取增量d1(d1<n)把全部记录分成d1个组,所有间隔为d1的记录分在同一组,在各个组中进行直接插入排序。
2)第二趟取增量d2(d2<d1),重复上述的分组和排序。
3)依次类推,直到所有的增量dt=1(dt<d(t-1)<...<d2<d1),所有记录在用一组中进行直接插入排序为止。
例 已知待排序记录的关键字序列为{49,38,65,97,76,13,27,49,55,04},请给出用希尔排序排序法进行排序的过程。
1)第一趟取增量d1=5,所有间隔为5的记录分在同一组,全部记录分成5组,在各个组中分别进行直接插入排序,排序结果如上图第2行所示。
2)第一趟取增量d2=3,所有间隔为3的记录分在同一组,全部记录分成3组,在各个组中分别进行直接插入排序,排序结果如上图第3行所示。
3)第三趟取增量d3=1,对整个序列进行一趟直接插入排序,排序完成。
希尔排序的算法实现如下所示,预设好的增量序列保存在数组dt[0..t-1]中,整个希尔排序算法需执行t趟。从上述排序过程可见,直接排序算法可以看成一趟增量是1的希尔排序,所以可以改写直接排序算法,得到一趟希尔排序ShellInsert。在ShellInsert中,具体改写主要有两处:
1)前后记录位置的增量是dk,而不是1;2)r[0]只是暂存单元,不是哨兵。当j<=0,插入位置已找到。
[算法描述]void ShellInsert(int r[],int dk,int Length)
{
/* 对顺序表r做一趟增量是dk的希尔排序 */
int i,j;
for(i = dk+1;i < Length; ++i)
{
if(r[i]<r[i-dk]) /* 需将r[i]插入有序增量子表 */
{
r[0]=r[i]; /* 暂时存在r[0] */
for(j = i-dk;j > 0 && r[0] < r[j]; j -= dk)
r[j+dk]=r[j]; /* 记录后移,直到找到插入位置 */
r[j+dk]=r[0]; /* 将r[0]即原r[i],插入到正确位置 */
}
}
}
void Shellsort(int r[],int dt[],int t,int Length)
{
/* 按增量序列dt[0..t-1]对顺序表L作t趟希尔排序 */
for(int k=0;k<t;++k) /* 一趟增量为dt[t]的希尔插入排序 */
ShellInsert(r,dt[k],Length);
}
[算法分析]
1.时间复杂度
当增量大于1时,关键字较小的记录就不是一步步地挪动,而是跳跃式地移动,从而使得在进行最后一趟增量为1的插入排序中,序列已基本有序,只要作记录的少量比较和移动即可完成排序,因此希尔排序的时间复杂度较直接插入排序地。当要具体分析,则是一个复杂的问题。
2.空间复杂度
从空间来看,希尔排序和前面两种排序方法一样,也只需要一个辅助空间r[0],空间复杂度为O(1)
[算法特点]1)记录跳跃式地移动导致排序方法是不稳定的。
2)只能用于顺序结构,不能用于链式结构。
3)增量序列可以有各种取法,当应该使增量序列中的值没有除1之外的公因子,并且最后一个增量必须等于1。
4)记录总的比较次数和移动次数都比直接插入排序要少,n越大时,效果越明显。所以适合以初始记录无序、n较大时的情况。
[完整代码]#include<iostream>
using namespace std;
void ShellInsert(int r[],int dk,int Length)
{
/* 对顺序表r做一趟增量是dk的希尔排序 */
int i,j;
for(i = dk+1;i < Length; ++i)
{
if(r[i]<r[i-dk]) /* 需将r[i]插入有序增量子表 */
{
r[0]=r[i]; /* 暂时存在r[0] */
for(j = i-dk;j > 0 && r[0] < r[j]; j -= dk)
r[j+dk]=r[j]; /* 记录后移,直到找到插入位置 */
r[j+dk]=r[0]; /* 将r[0]即原r[i],插入到正确位置 */
}
}
}
void Shellsort(int r[],int dt[],int t,int Length)
{
/* 按增量序列dt[0..t-1]对顺序表L作t趟希尔排序 */
for(int k=0;k<t;++k) /* 一趟增量为dt[t]的希尔插入排序 */
ShellInsert(r,dt[k],Length);
}
int main()
{
int dt[3]={5,3,1}; /* 增量没有除1之外的公因子 */
int a[11]={0,49,38,65,97,76,13,27,49,55,8}; /* a[0]是监视哨 */
Shellsort(a,dt,3,11);
for(int i=1;i<11;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
[运行结果]