希尔排序
按理解的话,其实希尔排序就是插排的升级版,他的思想是:对需要排序的数据进行分组,通过每组的插排使得组内有序,从而整体接近有序,此时对整体进行插排就很快(在插排中说过,插排,数据越有序速度越快)
但是要注意的一点是,分组不是常规的认为那样分组(如上所示),上图在红蓝组内有序后,整体数据如下
如此一看,整体并非有序,所以应该如下进行分组
组内有序后 ,整体如下
整体接近有序,达到要求希尔排序是结合了一定的数学问题的,数学问题给出了分组的方式,原理这里不做探讨,给出,尽量以素数分组,且最后一次分组每组个数为1,也就是最后一次是对整体进行排序,例如我在测试的时候用的数列就是{ 5, 3, 1 }.
代码实现
这里要说的是,如上数据,分两组,第一组元素就是第一个,第三个,第五个,第七个,第二组是第二个,第四个,第六个,第八个;逐步后移两步,由此可知,分几组就是后移几步
void shell(int *arr, int size, int step/*步数*/)//一趟希尔,插排变式
{
int tmp,i,j;
for(i = step/*组内第一个元素默认有序,从第二个开始*/;i < size; i += step)
{
tmp = arr[i];
for(j = i - step;j >= 0;j -= step)//注意上一个元素是当前序号减去步数,而非-1
{
if(arr[j] <= tmp)
break;//找到位置跳出
else
arr[j + step] = arr[j];//后移
}
arr[j + step] = tmp;//插入
}
}
void shellsort(int *arr,int size)//希尔排序
{
int d[] = {5,3,1};//分组数数列(示例)
for(int i = 0;i < sizeof(d)/sizeof(d[0]);i++)//循环遍历数列,进行每一趟的希尔
{
shell(arr,size,d[i]);
}
}
时间复杂度、空间复杂度以及稳定性
-
时间复杂度
先说说希尔排序与直接插排相比优点在哪。
假设一百个数据,直接插排根据O(n^2)计算,要进行10000次排序,我们100数据分十组组插排,每组组内有序排序十组就是进行101010=1000次,然后再进行全排,这时候因为组内有序,我们假设全排时间复杂度退化为O(n ^1.5),全排就需要1000次,这样总共需要2000次,相比直接插排快了不少,以此提速,我们的分组数列相比数据量的增加而言,增加是十分少的,所以可以忽略,数学方法归纳,希尔排序的时间复杂度一般为O(n ^1.3) -
空间复杂度
因为忽略分组数列的增长速度,我们的空间复杂度就是O(1) -
稳定性
正如上次讲到,希尔排序,跨越式分组,并且进行交换,所以他是不稳定的