算法3:希尔排序
基本思想
- 希尔排序是基于插入排序的一种快速排序算法
- 对于大规模乱序数组来说,插入排序很慢。因为它只会交换相邻的元素,因此元素只能一点点地从数组的一端移动到另一端。比如说,如果最小的元素恰好在数组的末尾,要将它挪到正确的位置,插入排序要进行N-1次挪动。
- 希尔排序是为了加快插入排序,每次不再只和相邻的元素交换,而是每次交换不相邻(间隔为h)的元素来对数组进行局部排序,h逐渐递减为1,当h=1时,希尔排序实际上退化为插入排序,也就是说最终是使用插入排序将局部有序的数组排好序。
特点
- 希尔排序的思想是使任意间隔为h的元素有序。这样的数组称为h有序数组。举个例子,假如有下面这样的乱序数组,h取5。

则希尔排序第一次迭代会使得
第1位元素:8和第6(1+5)位元素:9,有序
第2位元素:1和第7(2+5)位元素:10,有序
第3位元素:5和第8(3+5)位元素:2,有序,变成2,5
第4位元素:6和第9(4+5)位元素:3,有序,变成3,6
第5位元素:7和第10(5+5)位元素:4,有序,变成4,7
希尔排序第一次迭代后的数组如下,有颜色的数字是发生了交换:

所以我们可以看到,如果h很大,我们就可以把元素一步移到很远的地方去,为实现更小的h有序创造方便,比如说将第8位元素直接经过一步交换直接移到了第3位。如果按照插入排序老老实实地进行相邻交换,可能的需要交换5次。 - 希尔排序中的h并不是固定不变的,h先取一个较大的值,然后逐渐递减(这个递减并不一定是每次减少1,有可能减少2),直到h变成1。当h=1时,希尔排序也就退化成插入排序了。
- 如何选取h的取值序列呢?这是一个复杂的数学问题。另外,希尔排序的算法性能不仅取决于h,还取决于h之间的数学性质。
代码实现
void swap(int *a,int i,int j)
{
int t=a[i];
a[i]=a[j];
a[j]=t;
}
void shellsort(int a[], int n)
{
int h=1;
while(h<n/3)
h=3*h+1;
while(h>=1)
{
for(int i=h;i<n;i++)
{
for(int j=i;j>=h&&a[j]<a[j-h];j-=h)
swap(a,j,j-h);
}
h=h/3;
}
}
亮点
- 希尔排序实际上是一个类似于插入排序但是用了不同增量h的过程
- 输入一个随机的排序数组,在数学上居然还是不知道希尔排序所需要的平均比较次数。