算法图解
算法基本步骤
首先,希尔排序是基于插入排序的一个时间复杂度为O(N*logN)的一个很牛的排序。
大家应该能注意到,图解中每一趟排序的时候有的数背景颜色是一样的,像这样背景颜色相同的数为一组,我们一共可以分gap组。
那么什么是gap呢,就是当你选出一个数后,这个数后面所有的每加gap个位置的数定为一组数,看下图解:
这里的9 6 4 1为一组数,那么相应的剩下的数也可分组:
剩余的8 5 3是一组,7 5 2是一组。可以看到就是gap组数。然后我们对每一组的数进行插入排序,就能使得每组的数都是有序的:
然后再把这些数弄回到数组中得到新的数组:
之后将gap减小,假如说现在gap减为2,继续分组:
对每一组的数进行插入排序:
然后再把这些数弄回到数组中得到新的数组:
可以看到跟前面没有变化,但是重点不是这,当我们把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)。
稳定性
不稳定。。。