算法介绍
希尔排序(ShellSort)的名字源于它的发明者Donald Shell,是直接插入排序算法的一种更高效的改进版本,它是一种插入排序算法,希尔排序也叫作缩减增量排序。
算法描述
希尔排序通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到比较距离减小为1,即比较相邻元素的最后一趟排序为止。
希尔排序可选用不同的增量序列(h1,h2,··· ,hk),只要最后一趟增量序列h1 = 1就可以,在使用增量hk的一趟排序后,对于每一个i都有a[ i ] <= a[ i + hk ],所有相隔hk的元素都被排序,此时称文件是hk排序的。
希尔排序的一个重要性质是:一个hk排序的文件(然后将是hk-1排序的)保持它的hk排序性。换句话说就是前面各趟排序的结果不会被后面的各趟排序打乱。
动图演示
算法分析
时间复杂度:O(N1.25~2)
空间复杂度:O(1)
稳定性:不稳定
虽然希尔排序编程简单,但运行时间分析却很困难。已证明使用希尔增量时希尔排序的最坏运行时间是O(N2)。对于不同的增量序列,即使是一个小的改变都可能严重影响算法的性能。
Hibbard提出的增量序列(1, 3, 7, ···,2k - 1)的希尔排序的最坏运行时间为O(N3/2)。
Sedgewick提出的增量序列(1, 5, 19, 41, 109,···),该序列中的项或者是9 · 4i - 9 · 2i + 1的形式,或者是4i - 3 · 2i + 1的形式。其最坏运行时间为O(N4/3)。可以通过将这些值放入一个数组中实现,虽然可能存在某个增量序列对希尔排序的运行时间有重大改进,但是,这个增量序列在实践中还是最为人们称道的。
希尔排序的性能在实践中是完全可以接受的,即使对于数以万计的N也是如此,编程简单的特点使得它成为对适度的大量输入数据经常选用的排序算法。
代码实现
// C++
void shellSort(int a[], int length) { // 希尔排序
for (int gap = length / 2; gap > 0; gap /= 2) { // 增量序列为N / 2递归到1
for (int i = gap; i < length; ++i) { // 遍历
int key = a[i]; // 记录准备插入正确位置的元素
int j = i; // 记录当前比较位置指针
while (j >= gap && key < a[j - gap]) { // 寻找插入位置
a[j] = a[j - gap]; // 元素后移
j -= gap; // 指针减gap,当前比较位置前移
}
a[j] = key; // 插入合适位置
}
}
}
参考
数据结构与算法分析(Java语言描述)
数据结构(C语言版)