希尔排序由多次插入排序组成,只是每次插入排序的增量不同。希尔排序使用一个增量序列 ,这个增量序列可以是任意的,只要保证
.但不同的增量序列间存在更好的一个序列。在以增量
进行一次排序后,对于每一个位置
,有
(
没有越界),在使用
进行排序之后,整个数组就变得有序。
希尔增量:选择为数组元素个数的一半,每执行完一次插入排序,就将增量缩小为它自身的一半,直到
.采用希尔增量的希尔排序时间复杂度为
。
Hibbard增量:,
,采用Hibbard增量的希尔排序时间复杂度为
,平均运行时间为
.
Sedgewick增量:,
,采用Sedgewick增量的希尔排序时间复杂度为
,平均运行时间为
.
对于如下数组的排序:
23 | 40 | 5 | 32 | 81 | 15 | 20 | 18 | 26 | 27 |
使用希尔增量对数组进行排序:
排序时,从数组下标
处开始排序(因为当
时,这个元素之前没有跟它间隔
的元素。也就是在这趟排序时,P的初值
,并完成一次增量为
插入排序(也就是间隔为
元素之间的排序)。这次插入排序结果如下:
15 | 20 | 5 | 26 | 27 | 23 | 40 | 18 | 32 | 81 |
排序过程为:
P=2
5 | 20 | 15 | 26 | 27 | 23 | 40 | 18 | 32 | 81 |
p=3
5 | 20 | 15 | 26 | 27 | 23 | 40 | 18 | 32 | 81 |
p=4
5 | 20 | 15 | 26 | 27 | 23 | 40 | 18 | 32 | 81 |
p=5
5 | 20 | 15 | 23 | 27 | 26 | 40 | 18 | 32 | 81 |
5 | 20 | 15 | 23 | 27 | 26 | 40 | 18 | 32 | 81 |
p=6
5 | 20 | 15 | 23 | 27 | 26 | 40 | 18 | 32 | 81 |
p=7
5 | 20 | 15 | 23 | 27 | 18 | 40 | 26 | 32 | 81 |
5 | 20 | 15 | 18 | 27 | 23 | 40 | 26 | 32 | 81 |
5 | 18 | 15 | 20 | 27 | 23 | 40 | 26 | 32 | 81 |
p=8
5 | 18 | 15 | 20 | 27 | 23 | 32 | 26 | 40 | 81 |
5 | 18 | 15 | 20 | 27 | 23 | 32 | 26 | 40 | 81 |
p=9
5 | 18 | 15 | 20 | 27 | 23 | 32 | 26 | 40 | 81 |
排序后的结果如下:
5 | 15 | 18 | 20 | 23 | 26 | 27 | 32 | 40 | 81 |
最后一次插入排序之后,数组就变得有序,虽然最后一次排序是一个标准的插入排序,而插入排序的时间复杂度为,但由于最后一次排序之前,数组已经变得大体上有序,所以这次插入排序花费的时间并没有这么多。使用希尔增量的希尔排序性能并不是很好,使用Sedgewick增量的希尔排序的性能是更好的,并且在实践中使用Sedgewick增量的希尔排序要快于堆排序。(堆排序的时间复杂度为
.
使用Sedgewick增量的希尔排序实现:
//将Sedgewick增量序列列出,以便使用
const int sedgewick[] = { 1, 5, 19, 41, 109, 209, 505, 929, 2161,
3905, 8929, 16001, 36289, 64769, 146305,
260609, 587521, 1045505, 2354689, 4188161,
9427969, 16764929, 37730305, 67084289,
150958081, 268386305, 603906049, 1073643521 };
//希尔排序
void shell_sort(int* arr,int size){
int inc;//增量
int i,j;
int k;//用来选择Sedgewick增量
for(k=1,inc=sedgewick[k];k>=0;k--){//根据数组元素的大小,找到不大于size-1的sedgewick增量的下标,如果是10个元素排序,则从5开始
for(i=inc;i<size;i++){//从这里开始,下面的代码与插入排序的代码相似
int tmp=arr[i];
for(j=i;j>=inc&&arr[j-inc]>tmp;j--){
arr[j]=arr[j-inc];
}
arr[j]=tmp;
}
}
}