算法思想
希尔排序是特殊的插入排序,直接插入排序每次插入前的遍历步长为1,而希尔排序是将待排序列分为若干个子序列,对这些子序列分别进行直接插入排序,当每个子序列长度为1时,再进行一次直接插入排序时,结果一定是有序的。
常见的划分子序列的方法有:初始步长(两个子序列相应元素相差的距离)为要排的数的一半,之后每执行一次步长折半。
思路
- 先追求表中元素的部分有序,再逐渐逼近全局有序
- 更适用于基本有序的排序表和数据量不大的排序表,仅适用于线性表为顺序存储的情况
预排序:
将无序序列以d为间隔进行分组,所有间隔相同的为一组,分别对每组进行直接插入排序。
- 本次需要排序的数的数量为n,n为10。
- 先进行预排序,使原序列更接近有序序列,最后将预排之后的序列进行直接插入排序。
- d的初值为需要排序的数的数量的一半,d=n/2,d=5,就是相隔五个数的值作为一个子组,先对子组的数值进行插入排序
图解
- 第一次排序时,d=n/2=5,每次间隔5个数的值组成一个子组,对子组的数据进行插入排序
- 第二次排序时,d=d/2=2, 每次间隔2个数的值组成一个子组,对子组的数据进行插入排序
- 第3次排序时,d=d/2=1, 最后当d=1时,完成了预排序,最后进行一遍插入排序,就可以完成本次的希尔排序
代码演示
#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, d; // d 为步长
for (d = len / 2; d > 0; d /= 2) { // 步长初始化为数组长度的一半,每次遍历后步长减半,
for (i = 0; i < d; ++i) { // 变量 i 为每次分组的第一个元素下标
for (j = i + d; j < len; j += d) { //对步长为d的元素进行直插排序,当d为1时,就是直插排序
tmp = a[j]; // 备份a[j]的值
k = j - d; // j初始化为i的前一个元素(与i相差d长度)
while (k >= 0 && a[k] > tmp) {
a[k + d] = a[k]; // 将在a[i]前且比tmp的值大的元素向后移动一位
k -= d;
}
a[k + d] = tmp;
}
}
}
}