直接插入排序与希尔排序

直接插入排序
基本思想:在插入第i(i>1)个记录时,前面的i-1个记录已经拍好序
在这里插入图片描述
如图,前面比ri大的数就往后移,反之不动
而在排序时,就把第一个数看为有序序列,然后进行后面的操作
要注意这里的a[0]有妙用
保存要插入的数的值,不然你一插入,数据肯定往后移,那不就覆盖了吗
而且,值保存住了,才好判断数组里的数要不要往后移
上代码

#include<stdio.h>
void insertsort(int a[],int n) {
    
    
	int i,j;
	for(i=2; i<=n; i++) {
    
    
		a[0]=a[i];
		j=i-1;
		while(a[j]>a[0]) {
    
    
			a[j+1]=a[j];
			j--;
		}
		a[j+1]=a[0];
	}

}
int main() {
    
    
	int a[100];
	int n,i,j;
	scanf("%d",&n);
	for(i=1; i<=n; i++)
		scanf("%d",&a[i]);
	insertsort(a,n);
	for(i=1; i<=n; i++)
		printf("%d ",a[i]);
}

注意输入要从a[1]开始输,循环从i=2开始,a[0]最开始保存的a[2],然后a[1]和a[0]做比较,然后就都是一样的,另外,插入排序时间复杂度为n的平方。它只适用于待排序列基本有序或待排数的数组下标较小时,否则它的效率还是底地不失所望

2.希尔排序
插入排序虽然思想简单,但有两个弊端
1.当序列的有序性很差时,效率不得不低
2.当序列要变动的数的数组下标大时,循环次数也很多
希尔为了解决这两个问题,就想出了一个好办法,整个难搞,那我分组搞啊
但是,怎么分呢?
首先,分组待排序序列的目的是
1.减少待排序记录的个数
2.使整个序列向基本有序发展
分组啊,一般一上脑就是“逐段分组”,也就是如下图的分法
乍一眼看上去好像满足了上面讲的两个条件哦,但是你再仔细想想,这种搞法真的减少了待排序记录的个数吗
就拿第一个元素40为例,就算现在各个组经过一波操作都有序了,它也在第一组,最后40还是要去第3组,还是要经过好多次交换。
插入排序要避免的就是一个一个交换好久,其实在我看来,这种分组不仅不能提高效率,可能还比不上不分组,因为到最后要移动的数也没怎么变
但是呢,换一种分法就会好很多啦
如图
在这里插入图片描述
同一个元素的为一组
那么如此一来,大数很快就能跑到后面去了,毕竟一交换就跨越了一个组的长度
希尔的分法是初始d=n/2(d为整型),每个组内排好一次d就再除以2,d大于等于1
d等于1时就是序列大致有序了,进行整个序列的排列
一分组,单个组的元素数目变小了,效率当然就上来了,就好比一个人要搬的砖分给好几个人搬了,你说能不轻松很多吗。
而且单个单个的组排完序后,虽然整个序列还不是有序的,但是其有序性相比最开始绝对大大提升了不是吗
那有序性一提升,就又适合直接插入排序了不是吗
所以你看,希尔老人家一搞,序列最开始分的组多,单个组元素少,适合直接插入排序,插着插着有序性增强,又一次适合直接插入排序。如此一来,直接插入排序的两个弊端就被解决掉了,从而大大提升效率
最后总结一下,希尔排序的基本思想:
将整个待排序记录分割成若干个子序列,在子序列内分别进行直接插入排序,待整个序列中的记录基本有序时,对全体记录进行直接插入排序
上代码

#include<stdio.h>
void xier(int *a,int n) {
    
    
	int i,j,d;
	for(d=n/2; d>=1; d=d/2) {
    
    
		for(i=1+d; i<=n; i++) {
    
    
			a[0]=a[i];
			j=i-d;
			while(j>0&&a[j]>a[0]) {
    
    
				a[j+d]=a[j];
				j=j-d;
			}
			a[j+d]=a[0];
		}
	}
}
int main() {
    
    
	int a[100];
	int i,n;
	scanf("%d",&n);
	for(i=1; i<=n; i++) {
    
    
		scanf("%d",&a[i]);
	}
	xier(a,n);
	for(i=1; i<=n; i++)
		printf("%d ",a[i]);
}

一开始我还想着既然分了组,那就先 排完第一组再排完第二组这么排下去,后来发现大可不必,既然组与组之间互相独立,互不影响,那完全可以比完一组的第一个数就立马去操作第二组的数,反正最后全部组都排完就行。
事实上,这种思路也好写代码地多,只需直接插入排序一些+1的部分改为+d就差不多了。另外注意在希尔排序里j是有可能为负数的,所以循环条件里再加一个j>0
最后再来稍微演示下过程吧
在这里插入图片描述
首先a[0]=a[5],a[5]与a[1]比较,也就是处理第一组
然后i++,a[0]=a[6],a[6]与a[2]进行比较,也就是处理第二组
再然后i++,a[0]=a[7],a[7]与a[3]进行比较,也就是处理第三组
然后省略若干字,就当各组d=4时已经排序好了,那么再进行d=2时的分组和排序
然后就懒得说了,反正就这个意思,你再看看代码会懂的
顺嘴一提,可能有朋友疑惑,希尔都整三个循环了,怎么时间效率还高了呢?
嗯…其实我们平时的时间复杂度就只是估计,何况万物奥妙,岂是几个循环就能定死的东西?
从理论上讲,效率提高了,事实上也是这样。数学家已经证明希尔排序的时间复杂度大约是n的1.3次方,比直接插入排序的n的2次方已经好太多了
上面部分内容引用了B站上懒猫老师的视频,其他的加入大量自己的思考,希望对阅读者有帮助

Guess you like

Origin blog.csdn.net/heipao17/article/details/116568258