希尔排序又称为缩小增量排序,是1959年由D.L.Shell提出来的,它也是一种插入排序类方法,但在时间效率上 ,较直接插入排序和折半插入排序有较大的改进。
(1)算法思想
不断的把待排序的一组记录按照间隔值分成若干小组,然后对同一组的记录进行排序。具体做法如下:
a、取定一个正整数d1(d1<n)作为间隔值(或称为步长),然后把全部n个记录按照此间隔值从第一个记录起进行分组,每组记录的下标相差d1,对每组中的记录进行排序(排序方法任选,一般常用直接插入排序法)。
b、取定一个正整数d2(d2<d1)作为新的间隔值,对所有记录进行重新排序,再对各组进行组内排序;
c、重复以上操作,直到di=1为止,即间隔为1时记录有序,也就是整体达到有序。
特点:
子序列的构成不是简单地“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列。
对间隔值的取法有多种,希尔提出的取法是:d1=n/2,di+1=di/2。另外还有克努特提出的di+1=di-1/3.。下面 按照希尔排序的方法举例说明。
已知一组记录的关键字值为(43,25,39,29,65,57,76,22,41,39)
由于元素个数n=10,根据希尔法,取间隔值为d1=10/2=5,d2=5/2=2,d3=2/2=1,排序的具体过程如下:
第一趟 d1=5 43 25 39 29 65 57 76 22 41 39
|__________________| (43 57)
|__________________| (25 76)
|___________________| (39 22)
|___________________| (29 41)
|__________________| (65 39)
第一趟排序结果 43 25 22 29 39 57 76 39 41 65
第二趟 d2=2 |_______|_______|_______|_______| (43 22 39 76 41)
|_______|_______|_______|_______| (25 29 57 39 65)
第二趟排序结果 22 25 39 29 41 39 43 57 76 65
第三趟 d3=1 |___|___|___|___|___|___|___|___|___| (22 25 39 29 41 39 43 57 76 65)
第三趟排序结果 22 25 29 39 39 41 43 57 65 76
注:对括号内的关键字值进行直接插入排序
由以上实例可以看出,希尔排序每一趟以不同的间隔距离d进行分组排序。当d较大时,被移动的记录是跳跃式进 行的。到最后一次排序时(d=1),许多记录已经有序,并不需要多少移动,所以提高了排序的速度。
用C语言实现的希尔排序算法如下:
#include <stdio.h>
void ShellSort(int r[],int n)
{
int i,j,d;
d=n;
do{
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-=d;
}
r[j+d]=r[0]; //插入记录
}
}while(d!=1);
}
int main()
{
int a[101],n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
ShellSort(a,n);
for(i=1;i<=n;i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
(2)算法性能分析
希尔排序的速度一般要比直接插入排序快,但希尔排序是一个比较复杂的问题,因为时间的复杂度依赖于所取增量序列,截止目前,增量的选择无一定论。如果按照希尔法来取,每次后一个增量是前一个增量的1/2,则经过t =log2(n+1)次以后,dt=1,这时该算法的时间复杂度为O(nlog2n)。
根据上述实例分析希尔排序可知,相同关键字的记录在排序后的前后顺序有了改变,所以,希尔排序是一种不稳定的排序方法。