希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。
其实直接插入排序并不是那么逊的,它在待排序数据基本有序并且数量较少的时候威力还是很大的,甚至比一些高级算法还要高效.对于第二点,我只能说这就是我们shell算法的牛逼的地方了,插入排序每次只能移动数据一位,而shell算法成功的解决了这个问题.
shell算法的核心还是分组,但是这个分组就有门道儿了,因为它会实现取一个小于总数据长度的整数值gap作为分组的步长,什么意思呢?假如我们的待排序数组为:
序号 1 2 3 4 5 6 7 8 9 10
49,38,65,97,76,13,27,49,55,04
设置gap的值为长度10的一半也就是5,那么第一个和第六个元素就是一组,第二个和第七个元素就是一组,第三个和第八个元素就是一组,第四个和第九个元素就是一组,第五个和第十个元素就是一组,所以一共分为了gap = 5组,
组 一 二 三 四 五
序号 1 6 2 7 3 8 4 9 5 10
数据 49 13 38 27 65 49 97 55 76 04
交换后 13 49 27 38 49 65 55 97 04 76
然后如上面每一组之间进行再直接插入排序,比较如果前一个元素比较大,则交换两个元素的位置,直至5组全部交换完毕.此时数组的顺序为
13 27 49 55 04 49 38 65 97 26.
然后gap的值再减半为2,重新分组,也就是第一个 第三个 第五个 第七个 第九个 元素为第一组是13 49 4 38 97, 第二个 第四个 第六个 第八个 第十个元素为一组是27 55 49 65 26.
组 一 二
序号 1 3 5 7 9 2 4 6 8 10
数据 13 49 04 38 97 27 55 49 65 26
交换后 04 13 38 49 97 26 27 49 55 65
然后如上面对它们两个组分别进行直接插入排序,得到结果为
4 26 13 27 38 49 49 55 97 65,
之后gap的值再减半为1(要知道gap的值小于1的时候在分组就没意义了,一位你的每一个组至少要有一个元素才能组成一个序列.)这次我们直接对上一次的结果进行一次真正的直接插入排序(为什么说是真正的呢,因为此时步长已经为1)直至得出最终结果:
4 13 26 27 38 49 49 55 65 97.
下面是shell算法的实现代码:
#include <stdio.h>
#include <malloc.h>
void shellSort(int *a, int len); // 函数声明
int main(void)
{
int i, len, * a;
printf("请输入要排的数的个数:");
scanf("%d",&len);
a = (int *)malloc(len * sizeof(int)); // 动态定义数组
printf("请输入要排的数:\n");
for (i = 0; i < len; i++) { // 数组值的输入
scanf("%d",&a[i]);
}
shellSort(a, len); // 调用希尔排序函数
printf("希尔升序排列后结果为:\n");
for (i = 0; i < len; i++) { // 排序后的结果的输出
printf("%d\t",a[i]);
}
printf("\n");
return 0;
}
void shellSort(int *a, int len)
{
int i, j, k, tmp, gap; // gap 为步长
for (gap = len / 2; gap > 0; gap /= 2) { // 步长初始化为数组长度的一半,每次遍历后步长减半,
for (i = 0; i < gap; ++i) { // 变量 i 为每次分组的第一个元素下标
for (j = i + gap; j < len; j += gap) { //对步长为gap的元素进行直插排序,当gap为1时,就是直插排序
tmp = a[j]; // 备份a[j]的值
k = j - gap; // j初始化为i的前一个元素(与i相差gap长度)
while (k >= 0 && a[k] > tmp) {
a[k + gap] = a[k]; // 将在a[i]前且比tmp的值大的元素向后移动一位
k -= gap;
}
a[k + gap] = tmp;
}
}
}
}