本章节主要证明对包含n个元素的输入序列来说,任何比较排序在最坏情况下都要经过omega(nlgn)次比较。从而证明归并排序和堆排序是渐近最优的。同时,介绍了三种线性时间复杂度的排序算法:计数排序、基数排序和桶排序。
1. 排序算法的下界
在确定排序算法的下界时,借助决策树模型。决策树模型是一棵完全二叉树,它可以表示在给定输入规模情况下,某一特定排序算法对所有元素的比较操作。对于比较操作,假定元素互异,因此仅使用小于等于和大于比较操作符。典型的决策树模型如下图所示:
显然,n个元素,n!种不同排列,均可以为叶子结点。因此,有
n! <= l <= 2^h,(l为从根结点到叶子结点的路径长度,即为比较次数)。
故h >= lg(n!) = omega(nlgn)。
8.1-1
解 叶节点的最小深度为n-1,即有序的排列。
8.1-3
证明 (1)因为n!/2 <= l <= 2^h,所以h>=nlgn-nlge-1
(2)因为n!/n <= l <= 2^h,所以h>=nlgn-nlge-lgn
(3)因为n!/2^n <= l <= 2^h,所以h>=nlgn-nlge-n
所以h=omega(nlgn)无法达到线性。
8.1-4
证明:先分析题目,n个元素,由n/k个子序列构成,子序列内部无序,但是子序列之间是有序的。因此,对长度为n的序列的排序转化为对n/k个由k个元素构成的子序列进行排序。证明比较次数的下界是omega(nlgn)。
首先对于k个无序元素进行排序,比较次数的下界为omega(klgk),n/k个子序列的比较次数为omega(nlgk)。
让后还需要考虑,对n个元素确定这n/k个子序列执行的比较次数。因为k\n,不妨令n=km,则
(k!)^(n/k) <= l <= 2^h,故h>=mlg(k)!,所以h>=omega(mklgk)=omega(nlgk)
因此总的比较次数为omega(nlgk)。
2. 计数排序
计数排序假设n个输入中的每一个都是0到k区间的一个整数,其中k为某个整数。当k=O(n)时,排序的运行时间为theta(n)。
计数排序的基本思想是:对每一个输入元素x,确定小于x的元素个数。利用这一信息,就可以直接把x放到它在输出数组中的位置了。当存在相同元素时,需要从后向前扫描,当确定好当前元素位置后,需要调整计数个数。代码实现如下:
1 #define MAXN 105 2 #define MAXK 100 3 4 int A[MAXN], B[MAXN]; // A[]: original numbers, B[]: sorted numbers. 5 6 void CountingSort(int A[], int B[], int n, int k) { 7 int C[MAXK+1]; 8 int i, j; 9 10 for (i=0; i<=k; ++i) 11 C[i] = 0; 12 13 for (j=1; j<=n; ++j) 14 ++C[A[j]]; 15 // C[i] now contains the number of elements equal to i. 16 17 for (i=1; i<=k; ++i) 18 C[i] += C[i-1]; 19 // C[i] now contains the number of elements less than or equal to i. 20 21 for (j=n; j>=1; --j) { 22 B[C[A[j]]] = A[j]; 23 --C[A[j]]; 24 } 25 }
8.2-4
解:就是计数排序中1~19行程序,先计算得到数组C。所求[a..b]区间的个数就是C[b]-C[a-1]。
时间复杂度T(n) = theta(n) + theta(k) = theta(n+k)。
3. 基数排序
基数排序(radix sort),即给定n个d位数,其中每个数位有k个可能的取值。对于基数排序的d次循环的每一次循环,使用复杂度为theta(n+k)的稳定排序(如计数排序),则基数排序的时间复杂度为theta(d(n+k))。代码实现如下:
1 void CountingSort(int A[], int B[], int n, int k, int in) { 2 int C[MAXK+1]; 3 int i, j, tmp, mod=pow(k, in), div = mod/k; 4 5 for (i=0; i<k; ++i) 6 C[i] = 0; 7 8 for (j=1; j<=n; ++j) { 9 tmp = A[j]%mod/div; 10 ++C[tmp]; 11 } 12 13 for (i=1; i<k; ++i) 14 C[i] += C[i-1]; 15 16 for (j=n; j>=1; --j) { 17 tmp = A[j]%mod/div; 18 B[C[tmp]] = A[j]; 19 --C[tmp]; 20 } 21 } 22 23 void RadixSort(int A[], int n, int d) { 24 int i; 25 int B[MAXN]; 26 27 for (i=1; i<=d; ++i) { 28 if (i & 1) 29 CountingSort(A, B, n, 10, i); 30 else 31 CountingSort(B, A, n, 10, i); 32 } 33 34 if (d & 1) { 35 for (i=1; i<=n; ++i) 36 A[i] = B[i]; 37 } 38 }