希尔排序(Shellsort)

首先,Shell是发明这个算法的人名,不是这个算法的思想或者特点。

希尔排序,也称为增量递减排序。基本思路,是把原来的序列,等效视为一个矩阵的形式。矩阵的列数,也称为宽度或者增量,记为w。

假设数组A[n]以及矩阵B[][],对于两者的对应关系,可以记为A[k]=B[k/w][k%w]。也就是说,A中的元素会按照先行后列的顺序排列,即先左后右、先上后下的顺序放入矩阵B中。

对于增量或者说矩阵的宽度w,会有许多策略进行选择。假设w={1,2,4,8,16,32...}。从w的集合中选择小于数组元素数量n的最大元素w[i],并且选取w[0]到w[i]之间的所有增量,从w[i]开始,按照矩阵B中的每一列进行排序。这里的排序必须是输入敏感的,通常采用插入排序。本次完成后,取下一个w,再按照每列进行排序。直到w=1,矩阵退化为向量,排序完成。

 1 int max(int* H, int n, int size)
 2 {
 3     int i = 0;
 4     while (i < n&&size > H[i]) i++;
 5     return i - 1;
 6 }
 7 void shellSort(int* A, int lo, int hi)//[lo,hi)
 8 {
 9     int H[11] = { 1,5,19,41,109,209,505,929,2161,3905,8929 };
10     int size = hi - lo;
11     int gap = max(H,11,size);//选取小于规模的最大增量
12     for (int t = gap; t >= 0; t--)//从W[gap]一直到w=1,递减增量
13     {
14         int w = H[t];//选取步长,w即增量也是宽度(列数)
1516         for (int k = 0; k < w; k++)//对于每一列
17         {
18             for (int i = lo + w; i < hi; i += w)//下面的循环即插入排序
19             {
20                 int j = i - w;
21                 int tmp = A[i];
22                 while ((j >= lo) && (A[j] > tmp))
23                 {
24                     A[j + w] = A[j];
25                     j -= w;
26                 }
27                 A[j + w] = tmp;
28             }//一列的插入排序结束
29         }//每列都进行插入排序
30     }
31 }

 

希尔排序的总体策略,就是通过按列排序,使得每次排序后的局部有序性增加,最后达到全序列排序的目的。具体的证明可以参考《数据结构C++语言版》,有比较详细的h-有序和(g,h)-有序以及有序性增加的介绍。

下面是一个n=12,w{1,5}的过程。可以看到,w=5排序过后,有序性确实会有一定增加。

希尔排序的复杂度,取决于增量序列w的选择。几个比较优秀的序列,Pratt序列{1,2,3,4,6,8,9,12,..2^p*3^q,...}。这个序列的缺点很明显,会造成排序迭代次数很多,不过时间复杂度可以达到O(nlog^2n)。Sedgewick序列{1,5,19,41,109,209,505,929,2161,3905,8929...}的时间扶再度在最坏情况下可以为O(n^(4/3)),最好情况下为O(n^(7/6).

posted @ 2017-11-03 20:35  luStar  阅读(4054)  评论(0编辑  收藏  举报