【算法导论】学习笔记——第8章 线性时间排序

本章节主要证明对包含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 }

posted on 2014-08-29 16:03  Bombe  阅读(443)  评论(0编辑  收藏  举报

导航