排序算法(六)————希尔排序

希尔排序是对直接插入排序的一种改进,我们都知道直接插入排序在元素次序规则,数量较少时效率是最高的,而希尔排序恰巧合理的利用了这两点,对冒泡排序进行了改进,希尔排序的核心在于分组,什么是分组呢:

如下有10个元素:
12,15,8,66,24,55,34,99,3,10
我们假如要对其进行希尔排序,我们第一步要做的就是对其进行分组,那么分组依据是什么呢?

我们最常用的分组依据是lenth/2得到步长,何为步长?说通俗一点就是同一组元素之间的间隔大小,拿上面那个数列为例,10/2=5,当步长为5时,我们的元素被分成了5组分别为:

【12,55】 【15,34】 【8,99】【66,3】 和【24,10】

由此可见,步长实际上就是每一组相邻两个元素之间的位置差,比如第一组的12位置为0,55位置为5,他们位置之差就是步长5。

当然,分组实际上并不是真的就将这些元素拿出来分别装到五个数组里,而是一种逻辑分组,他们还是在原来的数组,原来的位置,只是在逻辑上把他们分到了一组而已。
分组完后我们要做什么呢?接下来就是分别在组内进行直接插入排序,排序完成后将步长再次减半,这样一个小组里元素会变多,并且每一个小组中元素会变得相对有序一些,一直到步长为1,这时候整个程序就变成了一个我们常用的插入排序,最后排序完成后即为结果。
这里需要注意的是我们在逻辑实现的时候并不是对这些组依次分别进行一个插入排序,我们是这样进行的:

每组第一个元素可以不用考虑,因为一个元素的时候是有序的。
从每组第二个元素开始依次进行插入排序,实现数列中每组元素中第一第二个元素有序化。
开始进行各个组第三个元素的排序,实现各个组内前三个元素的有序化。

一开始我在学习希尔排序时,我理解成了先把第一小组所有元素排好,再排第二小组所有元素,…
然后在看代码时一脸懵逼,完全不知道他们写的逻辑是什么,后来我终于反应过来并不是这样的,排序顺序是循环按照元素顺序着来的,先排所有小组第二个元素,再排所有小组第三个元素,…大家一定要理解这一块,这是比较容易把思路带到沟里的一点,说白了就是排序也是从前往后按照原数列顺序依次排的,并不是按照各个小组整体依次排序的。

下面我给出C语言实现的代码,实际上代码并不复杂,中间一段和插入排序很像,只是向前找元素的时候不是减一,而是减步长,因为你上一个元素和你现在要比较的值的位置差为一个步长,并且一开始所在位置位于r处,因为前r个元素是每个组第一个元素,他们实际上在每个组里本身就是有序的。

代码实现如下(代码经过测试,可以直接使用):

void shell(int arr[], int len);

void main()
{
	int arr[10] = {12,44,1,23,67,16,170,233,99,43};
	shell(arr,10);
	for (int i = 0; i < 10;i++)
	{
		printf_s("%d\n", arr[i]);
	}
}

void shell(int arr[],int len)
{
	int i;
	int r;
	//步长不断减小
	for (r = len / 2;r >= 1;r /= 2)
	{
		for (i = r;i < len;i++)
		{
			int key = arr[i];
			while (arr[i-r]>key && i>0)
			{
				arr[i] = arr[i - r];
				i = i - r;
			}
			arr[i] = key;
		}
	}
	
}

希尔排序时间复杂度和我们选择的增量有关,目前最好的时间复杂度为:O(n^1.3)
关于其时间复杂度的最优解,是一个尚未解决的数学难题,就看你了,骚年。

原创文章 24 获赞 2 访问量 923

猜你喜欢

转载自blog.csdn.net/weixin_43653187/article/details/104804880