前言
每个排序方法的速度都有各异,其中,希尔排序算是顶流,速度相当的快,但也相对难理解点,这篇文章将向你讲解希尔排序
一、希尔排序是什么?
希尔排序是基于直接插入排序延伸出来的一种排序,因为他的速度快而出名,总体思路就是将数组进行分组并进行预排序,然后再插入排序。
二、希尔排序的初阶
在学习希尔排序之前,需要先掌握直接插入排序的思想,否则理解起来将会相当的难
点击此处跳转到直接插入排序
我们需要再直接插入排序的基础上,对数组进行分组(规定每组相隔gap个元素)
gap越小,排序所需要的时间就越长,但是越接近有序
gap越大,排序所需要的时间就越短,但是越不接近有序
如图所见,我们已经将这个数组分成了红绿蓝三组
然后,我们对这三组进行单独的直接插入排序,排序完之后再依次放回原位
预排序的好处:
- 可以将数组变得相对有序
- 可以让那些大的数更快到最后,让那些小的数更快到前面
正常来说,9需要走动9次才能到达最后,但是现在只需要3次
0需要9次才能到达最前面,但是现在只需要3次
之后的话只需要进行插入排序就可以将数组排好序了(速度上比直接插入排序快上不少)
void ShellSort(int* arr, int size)
{
//确定好分多少组
int gap = 3;
//每一组都要进行排序
for(int j = 0; j < gap; j++)
{
//排一组(红、蓝、绿)
//因为等一下需要像直接插入排序那样访问最后一个数
//而最后一个数是end + gap
//为了防止越界访问,所以end的范围不能超过size - gap
for (int i = j; i < size - gap; i+= gap)
{
//i += gap是为了每次都是访问到自己颜色的那些元素
int end = i;
int tmp = arr[end + gap];//(要被排序的最后一个元素)
//控制排序的起点
while (end >= 0)
{
//升序
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
Swap(&arr[end + gap], &tmp);
}
}
//排序好之后就直接排序
InsertSort(arr, size);
}
另外,以上的代码也是可以进行优化的,就像是三个循环太多了,能不能让循环少一点?
第一种:
外面的两层循环:for(j = 0; j < gap; j++)
↓ {
↓ for(i = j; i < size - gap; i+=gap)
↓ }
↓ (红组先排,然后轮到蓝组,最后轮到绿组)
转化成一层
第二种:
for (i = 0; i < size - gap; i++)
这里运用的就是多组并排的方式
(因为直到最后,end每个位置都会出现一遍)
(倒不如直接排红组一轮到蓝组一轮到绿组一轮
再到红组二轮再到蓝组二轮......)
在这里,两种都是可以的,第一种的话更好理解,第二种的话比较巧妙
可以根据需求进行选择
三、希尔排序的进阶
虽说gap = 3看起来挺不错的,但是万一有一万个数据呢?
如果是一万个数据,分成三组,那每一组都有3333个元素
所以,gap必须和size(元素个数)有关
因此可以得出一种新的思路:
进行多次的预排序,多次改变gap,然后再直接插入排序
另外,当gap等于1的时候,就代表着直接插入排序了(因为gap代表分组的数量)
所以我们可以在改变gap的时候,让gap最后一定是为1的,这样的话也能省略掉直接插入排序函数引用的过程了
改变gap的方式:
int gap = size;
while (gap > 1)
{
//此处的/3 + 1就是为了让gap最后一个一定为1
//举例:如果没有+1,那么当数组的元素个数为6时
// 6/3 = 2; 2/3 = 0 (那就死循环了)
gap = gap / 3 + 1;
......
}
将以上代码进行合成就可以得到以下的完整代码了
四、完整代码
void ShellSort(int* arr, int size)
{
int gap = size;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = 0; i < size - gap; i++)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
Swap(&arr[end + gap], &tmp);
}
}
}
总结
以上就是今天要讲的内容,本篇文章着重讲解了希尔排序的原理和思路,在学习的路上还会有很多知识的分享,今后将会持续更新,敬请关注!