我们证明,使用增量$1, 2, 3, 4, 6, 8, 9, 12, 16, ..., 2^p*3^q$的Shell排序,时间复杂度为$O(n*{\log^2 n})$。
Edit:Latexified
Shell排序算法依赖一种称之为“排序增量”的数列,不同的增量将导致不同的效率。假如我们对20个数进行排序,使用的增量为1,3,7。那么,我们首先对这20个数进行“7-排序”(7-sortedness)。所谓7-排序,就是按照位置除以7的余数分组进行排序。具体地说,我们将把在1、8、15三个位置上的数进行排序,将第2、9、16个数进行排序,依此类推。这样,对于任意一个数字$k$,单看$A(k), A(k+7), A(k+14), ...$这些数是有序的。7-排序后,我们接着又进行一趟3-排序(别忘了我们使用的排序增量为1,3,7)。
只要排序增量的第一个数是1,Shell排序算法就是正确的。但是不同的增量将导致不同的时间复杂度。我们上面例子中的增量$(1, 3, 7, 15, 31, ..., 2^k-1)$是使用最广泛的增量序列之一,可以证明使用这个增量的时间复杂度为$O(n^{1.5})$。今天我们证明,使用增量$1, 2, 3, 4, 6, 8, 9, 12, 16, ..., 2^p*3^q$,时间复杂度为$O(n*{\log^2 n})$。
很显然,任何一个大于1的正整数都可以表示为$2x+3y$,其中$x$和$y$是非负整数。于是,如果一个数列已经是2-排序的且是3-排序的,那么对于此时数列中的每一个数$A(i)$,它的左边比它大的只有可能是$A(i-1)$。那么,在这个增量中的1-排序时每个数找插入位置只需要比较一次。一共有n个数,所以1-排序是$O(n)$的。
事实上,这个增量中的2-排序也是$O(n)$,因为在2-排序之前,这个数列已经是4-排序且6-排序过的,只看数列的奇数项或者偶数项(即单看每一组)的话就又成了刚才的样子。这个增量序列巧妙就巧妙在,如果我们要进行h-排序,那么它一定是$2h$-排序过且$3h$-排序过,于是处理每个数$A(i)$的插入时就只需要和$A(i-h)$进行比较。
现在,这个增量中的每一趟排序都是$O(n)$的,我们只需要数一下一共跑了多少趟。也就是说,我们现在只需要知道小于$n$的数中有多少个数具有$2^p*3^q$的形式。要想$2^p*3^q$不超过$n$,$p,q$的取值分别最多$O(\log n)$个,两两组合的话共有$O(\log n*\log n)$种情况。于是,这样的增量排序需要跑$O({\log^2 n})$趟,总的复杂度为$O(n*{\log^2 n})$。