排序算法—希尔排序

希尔排序

希尔排序(Shell’s Method)又称“缩小增量排序”(Diminishing Increment Sort),属于插入排序的一种,由D.L.Shell在1959年提出。希尔排序是对直接插入排序的改进,因为直接插入排序一次只将数据移动一个位置,如果数据离插入位置较远时,就需要多次比较和移动,希尔排序是将数据分组再排序,这样每组的元素少,比较和移动的次数也就少了,以此提升排序效率。

题目描述

给出一组数据,根据由小到大顺序输出。

输入要求:

输入一个整数n(数据长度)
输入n个数据

输出要求:

输出由小到大排序后的数据

样例输入:

10
37 28 46 19 55 28 92 84 63 71

样例输出:

19 28 28 37 46 55 63 71 84 92

基本思想

假设待排序的数据都存放在数组R[n]中,先取一个小于n的整数h1作为第一个增量,所有距离为h1的数据构成一个子序列,把序列分成若干子序列,对每个子序列进行直接插入排序;然后取下一个增量h2重复上述过程,直到h=1,对新序列进行直接插入排序。
如对于一个含有10个数据的序列,第一轮取h1=5,整个序列被分成{R0,R5},{R1,R6},{R2,R7},{R3,R8},{R4,R9} 5个子序列,对这5个子序列进行直接插入排序,再合成一个新的序列。
第二轮取h2=3,整个序列被分成{R0,R3,R6,R9},{R1,R4,R7},{R2,R5,R8} 3个子序列,对这3个子序列进行直接插入排序,再合成一个新的序列。
第三轮取h3=1,即整个序列只有{R0,R1,R2,R3,R4,R5,R6,R7,R8,R9} 这1个子序列,对这个序列进行直接插入排序,最后的结果就是有序序列。

希尔排序图解
可以看到,第一轮里只有1个子序列需要排序而且只需要比较和移动1次,第二轮里有2个子序列需要排序,子序列1中的28’和71只需比较和移动1次,子序列3中的37也只比较和移动1次,最终第三轮直接插入排序时,一共有3次循环需要插入,每次插入需比较和移动的距离也都很少。

参考代码(C语言)
#include<stdio.h>
void shell_sort(int R[],int n); //希尔排序 

int main()
{
	int i,R[100],n;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	scanf("%d",&R[i]);
 	shell_sort(R,n);
 	return 0;
} 

void shell_sort(int R[],int n)
{
	int i,j,k,h,t;    
	h=n/2;      //h是增量	
	do{
		for(i=0;i<h;i++)
			for(j=i+h;j<n;j=j+h)//对子序列直接插入排序,此时每次移动量为h 
			{
				t=R[j];
				k=j-h;
				while(t<R[k]&&k>=0)
				{
					R[k+h]=R[k];
					k=k-h;
   				}
   				R[k+h]=t;
   			}     //所有子序列排序完成,合成新序列,本轮结束 
 		h=h/2;    //增量取值为除以2向下取整
		} while(h>0);
 	for(i=0;i<n;i++)
 	printf("%d ",R[i]);
}
分析总结

希尔排序的时间性能是优于直接插入排序的,因为不是每个子序列都需要排序,这样在排序次数上得到了减少,并且每个子序列元素少于初序列,所以排序时需要比较和移动的次数也远少于初序列,在最后增量为1时,序列大部分元素已经趋于有序,所以排序的次数和难度也大幅度减少了。

经简单的上机实验验证,希尔排序的运行速度的却要比直接插入排序的快,我利用循环,输入1000,999,998……,1共1000个数,且都是反序。观察两个程序运行时间,看得出希尔排序比直接插入排序快了10ms左右,附图如下(左侧为直接插入排序,右侧为希尔排序)。

在这里插入图片描述

关于希尔排序的时间复杂度目前还没有明确的定论,因为增量的选择会影响希尔排序的性能,起初Shell提出的是除以2向下取整(希尔增量),后来也有说全取奇数,也有说取素数,可能根据不用的序列选择不同的增量会对希尔排序的性能有很大影响,但肯定优于直接插入排序的O( n 2 n^2 n2),目前有证明希尔排序平均时间复杂度可以降到O( n n n1.3)。

所以希尔排序的时间复杂度就暂时写为O( n n n1.3)~O( n 2 n^2 n2)
空间复杂度S( n n n)=O(1)

希尔排序是不稳定的,我们可以看到第一轮结束时,两个28的前后顺序出现了变化。
(排序的稳定性是指,初始序列中相同的数据在排序后的相对位置是否发生变化,如果变化,则排序不稳定。在本例中,虽然最后两个28的相对顺序没变化,但是在第一轮排序过程中,两个28的相对位置发生了变化,就说明排序有不稳定的风险,只是恰好之后的排序中,28的位置又发生了改变而已。可以验证序列:46 28 37 28’ 19初始增量为2,一轮排序后为28' 19 37 46 28,最后排序结果为19 28' 28 37 46,两个28的相对位置发生了变化。)

写在最后

代码的表达形式多种多样,重点是理解排序的思想和过程,附上一个网上看到的动画,可以帮助理解排序过程☛链接在此(个人觉得如果有一些基础的看本文上面给的图示去理解最好,更有助于建立编程的思维,视频能相对有些趣味性)
说明: 链接内视频不是本人制作,如侵权则删。当然网上的视频有很多,我只是找了一个相对简单明了的,大家也可自行搜索。

参考资料:《数据结构-用C语言描述》高等教育出版社

(只是分享个人学习时的想法和理解,如有问题还望大佬指点)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#include编程小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值