【算法导论】学习笔记——第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 }
时间: 2024-10-11 01:02:53

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

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

任意一种比较排序算法,在最坏情况下的运行时间下限是Ω(nlgn) 计数排序 假设n个输入元素中的每一个都是介于0到k之间的整数,k为某个整数,当k=O(n)时,计数排序的运行时间为Θ(n) 1 //输入数组A[1..n],存放排序结果数组B[1..n],临时存储区C[0..k] 2 COUNTING-SORT(A,B,k) 3 for i←0 to k 4 do C[i]←0 5 for j←1 to length[A] 6 do C[A[j]]←C[A[j]]+1 7 for i←1 to k

算法导论学习笔记——第12章 二叉查找树

二叉查找树性质 设x是二叉查找树中的一个结点,如果y是x的左子树中的一个结点,则k[y]<=key[x]:如果y是右子树中的一个结点,则k[y]>=k[x] 1 //中序遍历算法,输出二叉查找树T中的全部元素 2 INORDER-TREE-WALK(x) 3 if x!=nil 4 then INORDER-TREE-WALK(left[x]) 5 print key[x] 6 INORDER-TREE-WALK(right[x]) 查找 1 //递归版本 2 TREE-SEARCH(x,k)

算法导论学习笔记——第1章

所谓算法,就是定义良好的计算过程,它取一个或一组值作为输入,并产生出一个或一组值作为输出.亦即,算法是一系列的计算过程,将输入值转换成输出值. 一些常见的算法运行时间量级比较:对数级<多项式级<指数级<阶乘级 1 lgn < n 1/2 < n < nlgn < n 2 < n 3 < 2 n < n!

算法导论学习笔记——第3章

一些数学问题 1.对任意两个函数f(n)和g(n),f(n)=Θ(g(n))当且仅当f(n)=O(g(n))和f(n)=Ω(g(n)) 2.实数集有一个属性不能应用在渐进符号上 三分性:对于实数a和b,下列三种情况有且仅有一种情况成立,a>b,a=b,a<b 并不是所有的函数都可以进行渐进比较 3.近似的函数增长 对数<线性<多项式<指数<阶乘

算法导论学习笔记 第7章 快速排序

对于包含n个数的输入数组来说,快速排序是一种时间复杂度为O(n^2)的排序算法.虽然最环情况的复杂度高,但是快速排序通常是实际应用排序中最好的选择,因为快排的平均性能非常好:它的期望复杂度是O(nlgn),而且O(nlgn)中的常数因子非常小.另外,快速排序还可以实现原址排序,甚至在虚拟环境中也能很好的工作. 1 快速排序的描述 与归并排序一样,快速排序也使用了分治法的思想,下面是对一个典型的子数组A[p.. r]进行快速排序的分治过长: 分解:数组A[p.. r]被划分为两个(可能为空)子数组

算法导论笔记 第8章 线性时间排序

任何比较排序在最好情况下都要经过Ω(nlgn),即比较排序的下界为Ω(nlgn). 合并排序和堆排序都是渐进最优的. 要突破Ω(nlgn),就要进行非比较排序.计数排序.基数排序和桶排序都有非比较的一些操作来确定排序顺序,它们可以达到线性运行时间. 这三种排序都是以空间换时间.应用的不广,先不细看了. 原文地址:https://www.cnblogs.com/jackson-zhou/p/8419798.html

算法导论学习笔记——第11章 散列表

直接寻址表 1 DIRECT-ADDRESS-SEARCH(T,k) 2 return T[k] 3 4 DIRECT-ADDRESS-INSERT(T,x) 5 T[key[x]]←x 6 7 DIRECT-ADDRESS-DELETE(T,x) 8 T[key[x]]←nil

算法导论学习笔记——第6章

堆 堆数据结构是一种数组对象,可以被视为一棵完全二叉树. 对于给定的数组A,树的根为A[1],对于给定的下标为i的结点A[i],其父结点PARENT(i)=floor(i/2),左子结点LEFT(i)=2i,右子结点RIGHT(i)=2i+1 叶级结点的高度可以认为是0,每向上一层,高度加一,定义树的告诉为根结点的高度. P74

算法导论学习笔记——第4章

解递归式 1.代换法substitution 1)猜测解的形式 2)用数学归纳法找出使解真正有效的常数 2.递归树 使用递归树时,可以忽略一些“小误差”,将递归产生的结果作为猜测,用代换法进行验证. 也可以严格计算每一层递归树的代价,加总成递归式的结果. 对于有两个子问题,子问题规模为1/2的递归树(二叉树),树的高度是lgn,叶级节点的数量是n 3.主方法master method 递归式形式T(n)=aT(n/b)+f(n),其中a>=1,保证有一个及以上子问题:b>1,保证问题的规模逐步