首页 > 代码库 > 【算法导论】学习笔记——第8章 线性时间排序

【算法导论】学习笔记——第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         else31             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 }

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