初等排序之希尔排序

    昨天介绍了插入排序,今天介绍下简单插入排序的优化--------希尔排序。

    对于插入排序,我们其实不难发现如果数据本来就基本有序,那么数据插入移动的次数就会非常少。要注意这里这里说的基本有序不等于局部有序,例如下面的数列可以当做基本有序:{1,2,8,4,5,6,7,3},是整体而言,而局部有序的例子如下:{6,7,8,9,1,2,3,4,5},这里只是数据的部分有序,对于整体而言,其实移动次数不会减少太多。其实这也是非常不好量化的概念,总之,大家要知道的是,简单的插入排序是有办法优化的,而优化的方法就如下:

    将整个数列分成子序列,但要注意这里说的子序列并不是简单的连续逐段分割,而是离散化,具体的方法就是把相隔相同增量的数据记录变为一组,形成子序列。这样分割的方法就能保证每组排序好后,对于整体而言就是基本有序,而如果用逐段分割那么即便每组都有序了,那也是部分有序,大家可以参考刚才提到的的例子:{6,7,8,9,1,2,3,4,5},而对于增量的取法,可以使用希尔最早提出的方法d=n/2,之后每次增量减半向下取整,最后只要保证d=1即可。具体的例子如下:

   初始序列如下:

一共十个元素,取10=10/2=5,则分组情况如下(相同颜色的为一组):

很简单地能看到,分成了五组{59,14},{20,23},{17,83},{36,13},{98,28},之后对每一组利用简单的插入排序,只是不同的是间隔不是简单排序中的1,而是这里的d=5

在d=5时每组进行排序后,并以d=d/2=5/2=2 为新的间隔进行分组后,结果如下:

大家要注意,每组排序的结果不影响其他组的排序,比如{59,17}排序后变成{17,59},但是这里我们看到23<59仍然是在59后面的。

在d=2后,这时就只有两组{14,17,28,23,36},{20,13,59,83,98},这时再每组进行简单插入排序,和d=5时一样,不多BB.

以下为d=2排序后的结果和以新的d=d/2=2/2=1进行分组:

在d=1时,整个数列变为一组,下面进行的动作就是为简单插入的步骤,也就是说我们之前做的工作都是为了使数列更加基本有序,以便在进行d=1的简单插入排序时能大幅度的减少移动次数,以下是d=1排序后的结果,也就是最终的结果 :



相信到这里,大家可能会有困惑,在进行了大量的d不同排序后,最后还是要进行d=1的简单插入排序,明显比简单插入排序要更复杂啊。我要告诉大家的是,复杂是复杂,只不过这里的复杂是思路复杂,在对于计算机来说,其运行的效率其实是提高的。大家可以想象下,在一开始d很大时候,每组的数据很少,即便要把数据插入到每组第一个位置,移动的次数也不会很多,并且在多次的d变化后,在进行d=1的排序后,其实数列已经基本有序了,这时再进行简单的插入排序,移动的次数是大大减少了,效率也就高了。但是要注意的是,虽然移动的次数减少了,但是比较的次数确实是增加了,但是其增加的时间远小于移动次数减少带来的减少的时间,所以总体上,希尔排序效率是提高的了,事实证明,简单排序的时间复杂度是而希尔排序的时间复杂度在

以下是希尔排序的代码:

#include<stdio.h>
void Shell_Sort(int A[],int N)
{
	
	for(int d=N/2;d>=1;d=d/2)
	{
	   for(int i=d;i<N;i++)             //从每组的第二个元素开始插入,和简单插入排序一样,默认第一个元素是有序的 
	   {                                    
	   	   int v=A[i];                      //下面按照简单插入排序的方法,将要插入的数据提取出来 
		   int j=i-d;                       // 这里与简单插入排序不同 
		   while(j>=0 && A[j]>v)
		   {
		    	A[j+d]=A[j];
			    j-=d;
		   } 
		     A[j+d]=v;               
       } 
	}
}
int main()
{
	int N;
	int A[100];
	
	scanf("%d",&N);
	for(int i=0;i<N;i++)
	scanf("%d",&A[i]);
	
	Shell_Sort(A,N);
	
	printf("希尔排序\n");
	for(int i=0;i<N;i++)
	printf("%d  ",A[i]);
	return 0;
} 

PS:晚安~

O(


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值