希尔排序--思想及其代码实现

1,744 阅读5分钟

希尔排序的思想及代码实现

希尔排序的基本思想

希尔排序简单点来说,就是直接插入排序的优化,能比直接插入排序应对更多的情况,但两者还是有些区别:

ps:如果有不清楚直接插入排序的兄弟可以这篇文章,里面有详细的介绍: juejin.cn/post/715497…

在我们排序时,假设我们想用直接插入排序来排,但是要排序的数组刚好是个逆序的,那它的时间复杂度就会比较大,也就是说执行的过程会更多更繁琐,而为了优化这种排序的过程,我们可以先对这种数组进行预排序,预排序就是先让这个数组进行粗略的排序,目的就是让数组更接近有序,而不是一个完全的逆序数组,这样直接插入排序在执行的时候,效率就会变高,这就是希尔排序的思想。

那么怎么进行预排序呢?我们可以把他们分成几个组,来进行排序,比如下面这个数组(假设最后的结果为升序):

image.png

我们可以先令gap等于3(后面会解释gap到底怎么取值),意思就是把该数组每3个空分一个组,如下图(end为数组的下标):

image.png

在我们分好组之后,接下来就可以开始预排序了,也就是一组一组的排序,排序方式跟直接插入排序一样:

如上图所示,排序从蓝色分组中的第一组的第二个数5开始,跟直接插入排序一样,将前面的9看作已经排好的数组,5小于9,因此9要往后移,注意移动的时候是以gap为单位移动的;

蓝色这第一组排完序后,end++,开始对绿色分组中的第一组开始排序,tmp也变成的绿色这第一组中要插入的数据,完成这几步操作后数组的状态如下图所示: image.png

如上图所示,此时排序从绿色分组中的第一组开始,因此arr[end]指向该分组中的第一个元素1,tmp指向的7为此时要插入的元素,重复直接插入排序的步骤,1和7排完序后,end++,tmp则变成紫色这一组中要插入的数据,如下图所示:

image.png 用直接插入排序的方法,重复以上操作。

直到这一步(如下图所示):

image.png

首先我们还是用直接插入排序的方式来排,5小于9,因此9要往后移,5要往前移到9原来的位置,然后end以gap为单位,向前移到8的位置,再让tmp和8进行比较,然后重复上述操作,因为原来end是指的8的位置,因此在这个基础上,end++,如下图所示:

image.png 由此可以发现,tmp此时已经越界了,end所在的位置刚好是n-gap-1,因此可以将该条件作为排序终止的条件,此时gap为3的排序就完成了。

这时的数组已经接近有序了,但希尔排序并不是只需要排这一轮,gap的值在每一轮排序终止后都会减少,因为gap的值每减少一次,数组就会更接近有序,当gap值为1时,此时的希尔排序就相当于直接插入排序了,那么究竟怎么控制gap的值呢?

//如何控制gap的值:

int gap = n;

while (gap > 1)
{
	gap = gap / 2;
	//用于控制gap的值

	//此处省略排序的主体

}

如上述代码所示,我们可以用while来控制gap的值,gap的值在每一轮排序终止后,都会整除一次2,但是为什么是除2呢?

因为gap的值不管为多少,在整除n次2以后,最终的值都会变成1,满足直接插入排序的条件,比如当gap为5,整除2后会变成2,再整除2后就变成了1

希尔排序的代码实现

当我们了解上述希尔排序的主要思想后,就可以来写代码了,废话不多说,直接放代码:

#include<stdio.h>     /*printf*/

void PrintArray(int* arr, int len)    /*打印数组内容*/
{
	for (int i = 0; i < len; i++)
	{
		printf("%d  ", arr[i]);
	}
	printf("\n");
}

void ShellSort(int* arr, int n)      /*希尔排序*/
{
	int gap = n;
	//gap:分组的依据,gap的值每减小一次,排序就会越接近有序

	while (gap > 1)
		//当gap的值为1时,排序就相当于直接插入排序
	{
		gap = gap / 2;
		//控制gap的值,使其最终能够等于1,满足直接插入排序的条件

		for (int i = 0; i < n - gap; i++)
			//end排完序后的位置为n-gap-1,因此将n-gap作为终止条件

		{
			int end = i;
			int tmp = arr[end + gap];
			//将要插入的数据存到tmp当中

			while (end >= 0)
			{
				if (arr[end] > tmp)
				{
					arr[end + gap] = arr[end];
					//如果tmp的值较小,则arr[end]以gap为单位向后移动

					end = end - gap;
					//gap继续向前比较

				}
				else
				{
					break;
					//排序完成后退出循环

				}
			}

			arr[end + gap] = tmp;
			//此时tmp后面的值都比它大,前面要么没有数据,要么都比它小
			//tmp成功插入

		}
	}
	PrintArray(arr, n);         /*打印函数*/
}

int main()
{
	int arr[] = { 9,1,2,5,7,4,8,6,3,5 };

	int len = sizeof(arr) / sizeof(arr[0]);
	//计算数组的长度

	ShellSort(arr, len);       /*希尔排序*/

	return 0;
}

运行结果:

image.png


以上就是本篇文章的全部内容,如果你觉得对你多少有些帮助,可以点个赞或者收藏一波支持一下哦,欢迎各位大佬批评指正,咱们下次再见!