1.希尔排序(ShellSort):又称缩小增量排序,该方法因DL.Shell于1959年提出而得名。前面我们讲了插入排序,那么其实希尔排序就是在插入排序上延伸出来的一种更高效的排序算法,为什么说它更加高效呢?因为其实在元素个数非常少或者元素已经基本有序的情况下插入排序的速度是非常快的。那么究竟什么是希尔排序呢?希尔排序是基本排序算法中的一种,是一种直接插入排序优化后的排序方法,它通过设置步长进行分组,然后对每组中的元素进行插入排序,随着步长的减少,每组中的数据会随之增加,直到步长为1的时候,此时的排序进行完成。
我们来用图更直观的感受一下希尔排序:
我们可以看出,其实希尔排序就是开始以初始序列的一般为步长(增量),以后每次减半,直至步长为1,便排序完成。
2.代码实现:
void PrintSort(int arr[],size_t size)
{
size_t i=0;
for(; i<size; ++i)
{
printf(" %d ",arr[i]);
}
printf("\n");
}
void ShellSort(int arr[],size_t size)
{
size_t i;
size_t j=0;
size_t bound;
size_t gap;
int value;
if(size<=1)
{
return;
}
gap=size/2;
for(; gap>0; gap/=2)//生成步长序列(size/2,size/4,size/8...1)
{
bound=gap;
for(; bound<size; ++bound)//完成所有组所有元素的插入排序
{
//插入排序是从第二个元素开始的
//先处理第一组的第二个元素,再处理第二组的第二个元素,再处理第三个组的第二个元素...
//处理第一组的第三个元素,再处理第二组的第三个元素,在处理第三个组的第三个元素...
//...
value=arr[bound];
i=bound;
for(; i>=gap; i-=gap)//插入排序
{
if(arr[i-gap]>value)
{
arr[i]=arr[i-gap];
}
else
{
break;
}
}
arr[i]=value;
}
printf("第%d次的排序结果为:",++j);
PrintSort(arr,size);
}
}
int main()
{
int arr[]={8,56,43,28,2,66,87,42,96,3};
size_t size=sizeof(arr)/sizeof(arr[0]);
ShellSort(arr,size);
//PrintSort(arr,size);
system("pause");
return 0;
}
3.算法分析
时间复杂度:希尔排序的时间复杂度和步长有关,步长的选择是希尔排序的重要部分,最差情况下为:O(n^2),一般情况为:O(n*logn),最好可以达到:O(n^1.3);
空间复杂度:无额外开销。故空间复杂度为:O(N).
稳定性:不稳定。由于进行了多次插入排序,我们都知道插入排序是稳定的,不会改变相同元素的相对顺序,但是在不同的插入排序中,相同的元素在各自的插入排序中移动,相对顺序有可能会被打乱。所以Shell排序是不稳定的。那么此处我们还是通过图来更直观的了解一下。
4.总结
在希尔排序中,步长逐渐变小,最后变为1,当步长大的时候对每一组进行插入排序后,小的元素就被移动到了前面,序列大概基本有序,随着步长的逐渐缩小,只需要对序列进行微调即可,不会移动大量的元素。所以希尔排序是一种逐渐是序列基本有序的过程,它是插入排序的改进。