【排序】希尔排序

算法图解

算法基本步骤

首先,希尔排序是基于插入排序的一个时间复杂度为O(N*logN)的一个很牛的排序。

大家应该能注意到,图解中每一趟排序的时候有的数背景颜色是一样的,像这样背景颜色相同的数为一组,我们一共可以分gap组。

那么什么是gap呢,就是当你选出一个数后,这个数后面所有的每加gap个位置的数定为一组数,看下图解:

【排序】希尔排序_第1张图片

这里的9 6 4 1为一组数,那么相应的剩下的数也可分组:

【排序】希尔排序_第2张图片

剩余的8 5 3是一组,7 5 2是一组。可以看到就是gap组数。然后我们对每一组的数进行插入排序,就能使得每组的数都是有序的:

【排序】希尔排序_第3张图片

然后再把这些数弄回到数组中得到新的数组:

【排序】希尔排序_第4张图片

之后将gap减小,假如说现在gap减为2,继续分组:

【排序】希尔排序_第5张图片

对每一组的数进行插入排序:

【排序】希尔排序_第6张图片

然后再把这些数弄回到数组中得到新的数组:

【排序】希尔排序_第7张图片

可以看到跟前面没有变化,但是重点不是这,当我们把gap减为1时:

在这里插入图片描述

可以看到这组数已经非常接近有序了,这个时候我们再用插入排序的话,效率会大大提升。

整体思路

先选定gap,假设数据个数为n个。一般情况下:

  • gap的初始值为n。

  • gap每次变化的值为gap = gap / 2 或 gap = gap / 3 + 1(保证gap一定出现==1的情况)。

然后每次分出gap组,对每一组的数进行插入排序。直到当gap == 1的时候就是直接插入排序。

代码实现

//希尔排序
void ShellSort(int* a, int n)
{	
	int gap = n;//保证能够进入循环
	//如果此处gap为n/3+1或n/2的话就不能保证能进入循环
	//比如说n为2的时候
	while (gap > 1)
	{
		gap = gap / 3 + 1;//此处也可改为gap /= 2;
		for (int j = 0; j < gap; j++)
		{
			for (int i = j; i < n - gap; i += gap)
			{	//外面的这两层i,j循环也可以改为一层的,就是下面这个
				//for(int i = 0; i < n - gap; i++)//下面的这个完全是插入排序的方法了,基本是一模一样,就是把插入排序里面的1改为了gap
				int end = i;
				int key = a[end + gap];
				//单趟排序
				while (end >= 0)
				{
					if (a[end] > key)
						a[end + gap] = a[end];
					else
						break;

					end -= gap;
				}

				a[end + gap] = key;
			}
		}
	}
}

时间复杂度

希尔排序的时间复杂度的计算非常复杂,但是记住结论就行。大概在O(N^1.3)这个量级,不过也可以记成O(N * logN)。

稳定性

不稳定。。。

你可能感兴趣的:(数据结构,排序算法,算法,数据结构)