希尔排序是插入排序的一种,又叫缩小增量排序,也是对直接插入排序的一种优化,下面是插入排序的一些特征:
- 时间复杂度不确定,看具体实现,依赖于增量序列函数,本文实现的时间复杂度为O(n^2),若步长选取为 2^k+1, ..., 5, 3, 1 其时间复杂度为 O(n1.5),当n在某个特定范围时,有些实现可以达到O(n1.3)
- 空间复杂度,最好最坏平均都为:O(1)
- 是否稳定:不稳定
每次排序,数据都会变得更加有序
希尔排序的基本思想:先将待排序表每隔一定距离取一个数据,形成一个子表,整个表化为许多子表,然后对每个子表分别进行直接插入排序,步长越来越小,最后为1时,对表整个表进行一次直接插入排序。
有人可能会觉得这不是简单问题复杂化吗?并不是,因为直接插入排序算法适用于基本有序和数据量不大的排序表,适用于的意思就是更快,所以可以利用这两个特性对直接插入排序进行优化,将一个子表变成多个子表,就是在缩小数据量,子表有序后,整个表就更加有序了。
子表选取例子:原表L(1,2,3,4,5,6,7,8,9)有九个元素,步长 dk 取 9/2 即 4,得到 4 个子表 L1(1,5,9), L2(2,6), L(3,7), L(4,8)
下面是一种希尔排序的代码,增量选取为 n/2, n/4, ..., 2, 1:
/**
* arr 数组首地址
* len 数组长度
*/
void shell_sort(int *arr, int len)
{
int i, j;
int dk; // 记录步长
for (dk=len/2; dk>=1; dk>>=1) { // 步长变化
for (i=dk; i<len; i++) {
if (arr[i] < arr[i-dk]) {
int temp = arr[i];
for (j=i-dk; j>=0 && temp<arr[j]; j-=dk)
arr[j+dk] = arr[j];
arr[j+dk] = temp;
}//if
}//for
}//for
}
测试代码,可直接复制后编译执行:
#include <stdio.h>
void show(int *arr, int len);
void insert_sort(int *arr, int len);
int main()
{
int len = 7;
int arr[] = {7, 10, 11, 9, -8, 2, 27};
insert_sort(arr, len);
show(arr, len);
return 0;
}
/**
* arr 数组首地址
* len 数组长度
*/
void show(int *arr, int len)
{
int i;
for (i=0; i<len; i++) {
printf("%4d", arr[i]);
}
printf("
");
}
/**
* arr 数组首地址
* len 数组长度
*/
void shell_sort(int *arr, int len)
{
int i, j;
int dk; // 记录步长
for (dk=len/2; dk>=1; dk>>=1) { // 步长变化
for (i=dk; i<len; i++) {
if (arr[i] < arr[i-dk]) {
int temp = arr[i];
for (j=i-dk; j>=0 && temp<arr[j]; j-=dk)
arr[j+dk] = arr[j];
arr[j+dk] = temp;
}//if
}//for
}//for
}