《算法导论3rd第八章》线性时间排序

前言

之前的几种排序:快速排序、合并排序和堆排序,有一个有趣的性质:在排序的最终结果中,各元素的次序依赖于他们之间的比较。我们把这类算法叫做比较排序。使用此算法的最坏情况经过 Ω ( n l g n ) \Omega(nlgn) Ω(nlgn)次比较。此间要介绍三种线性时间复杂度的排序算法——计数排序、基数排序、桶排序

排序算法的下界

比较排序可以被抽象成一棵决策树。决策树是一棵完全二叉树,它可以表示在给定输入规模情况下,某一特定排序算法对所有元素的比较操作。
在这里插入图片描述
在决策树中,从根结点到任意一个可达叶结点之间最长简单路径的长度,表示的是对应的决策算法中最坏情况下的比较次数。因此,一个比较排序算法中的最坏情况比较次数就等于其决策树的高度。

最坏情况下,任何比较排序算法都需要做Ω(nlgn)次比较

证明: 对于一棵每个排列都作为一个可达叶结点出现的决策树,根据前面的讨论即可确定其高度。考虑一棵高 度为h的、具有l个可达叶结点的决策树,它对应于对n 个元素所做的比较排序。因为n个输入元素共有n!种排列,每一种都作为一个叶子出现在树中,故有n!≤l。又由于在一棵高为h的二叉树中,叶子的数目不多于2^h,则有
n ! ≤ l ≤ 2 h n! \leq l \leq 2^h n!l2h
对该式取对数,得到
h ≥ l g ( n ! ) = Ω ( n l g n ) h≥lg(n!)=Ω(nlgn) hlg(n!)=Ω(nlgn)

练习

在这里插入图片描述

1-1

最少进行n-1次比较(插入排序),所以深度最小是n-1。
Ps: 题目中问的最好情况,上诉定理是最坏情况的下界

1-2

(略)

1-3

(略)

1-4

因为每个子序列有k!种排列方式,那么n/k个子序列就有 ( k ! ) n / k (k!)^{n/k} (k!)n/k种排列方式,所以
( k ! ) n / k ≤ 2 h h ≥ ( n / k ) l g k ! l g k ! = Θ ( k l g k ) l g k ! ≥ k l g k (k!)^{n/k}≤2^h \\ h≥(n/k)lgk! \\ lgk!=Θ(klgk) \\ lgk!≥klgk (k!)n/k2hh(n/k)lgk!lgk!=Θ(klgk)lgk!klgk
所以 h ≥ ( n / k ) k l g k = n l g k h≥(n/k)klgk=nlgk h(n/k)klgk=nlgk

计数排序

计数排序假设n个输入元素中的每一个都是介于0到k之间的整数。 其基本思想就是对每一个输入元素x(小于k), 把x直接放到它在最终输出数组(长度为k)中的位置上。

Count-Sort(A, B, k)
1 for i <-- 0 to k
2 	do C[i] <-- 0
3 for i <-- 1 to length[A]
4 	do C[A[i]] <-- C[A[i]] + 1
5 for i <-- 1 to k
6 	do C[i] <-- C[i-1] + C[i]
7 for j <-- length[A] downto 1
8 	do B[C[A[j]]] <-- A[j]
9 		C[A[j]] <-- C[A[j]] - 1

在这里插入图片描述

  1. 计数排序时间代价:Θ(k+n), 实践中当k=O(n)时,运行时间为O(n)。
  2. 计数排序算法没有用到元素间的比较,它利用元素的实际值来确定它们在输出数组中的位置,不是一个基于比较的排序算法,从而它的计算时间下界不再是Ω(nlogn);
  3. 计数排序算法是一个稳定的排序算法, 即经计数排序,输出序列中值相同的元素之间的相对次序与他们在输入序列中的相对次序相同

练习

在这里插入图片描述

2-1

  1. C[1…k]被初始化为0。
  2. 用C统计A数组各值的量 C=⟨2,2,2,2,1,0,2⟩
  3. 计算出C统计的数,放回B时的偏移量C=⟨2,4,6,8,9,9,11⟩
  4. 根据C数组统计的量,放回B,B=⟨0,0,1,1,2,2,3,3,4,6,6⟩.

2-2

(略)

2-3

原数组靠前的相同元素出现在新数组靠后的位置上。所以就不稳定了。

2-4

计算出小于a值得元素个数C[a-1],小于等于b值得元素个数C[b],两者差值就是在[a,b]区间上的元素个数。

基数排序

基数排序(radix sort)是一种用在老式穿卡机上的算法。分别对各个位置上的数值进行排序,首先按最低有效位数字,以解决卡片排序问题。

Radix-Sort(A, d)
1 for i <-- 1 to d
2 	do use a stable sort to sort array A on digit i

在这里插入图片描述

1.给定n个d位数,每一个数可以取k中可能的值。如果所用的稳定排序需要Θ(n+k)的时间,基数排序算法能以Θ(d(n+k))的时间正确的对这些数进行排序。
2. 给定n个b位(2进制)数和 把其中r位正整数表示一元数值, 即 b/r 表示数为几元数值,即RADIX-SORT能在Θ( (b/r)*(n + 2^r) ) 时间内正确的对这些数进行排序

基数排序与比较排序

  • 如果根据常见的情况有b=O(lgn)并选择r≈lgn,则基数排序的运行时间为O(n),这看上去要比快速排序的平均情况时间O(nlgn)更好些。
  • 在这两个时间中隐含在O记号中的常数因子是不同的。对于要处理的n个关键字,尽管基数排序执行的遍数可能要比快速排序要少,但每一遍所需的时间都要长得多。
  • 此外,利用计数排序作为中间稳定排序的基数排序不是原地排序,而很多O(nlgn)时间的比较排序算法则可以做到原地排序。因此当内存容量比较宝贵时,像快速排序这样的原地排序算法可能是更为可 取的。

练习

在这里插入图片描述

3-1

在这里插入图片描述

3-2

  1. 归并和插入排序是稳定的,因为他们不改变相同元素原有的顺序。堆排序和快速排序是不稳定的。
  2. 如果想让以上4种排序都是稳定排序,那么我们保存原数组相同元素的下标,在进行比较排序时,不同元素肯定没有这个问题

3-3

(略)

3-4

可视为三位n进制数,进行特殊的基数排序。

3-5

最坏情况需要d轮排序

桶排序

桶排序的思想就是把区间[0,1)划分成n个相同大小的子区间,或称桶。然后,将n个输入数分布到各个桶中去。因为输入数均匀且独立均匀分布在[0,1)上,所以,一般不会有很多数落在一个桶中的情况。为得到结果,先对各个桶中的数进行排序,然后按次序把各个桶 中的元素列出来即可。

Bucket-Sort(A)
1 n<--length[A]
2 for i <-- 1 to n
3 	do insert A[i] into list B[floor(n*A[i])]
4 for i <-- 0 to n-1
5 	do sort list B[i] with insertion sort
6 concatenate the list B[0]...B[n-1] together in order

在这里插入图片描述
除第5行外,所有各行在最坏情况的时间都是O(n)。唯一需要分析的部分就在于第5行中插入排序所花的时间。桶排序的期望运行时间为Θ(n) + n*O(2- 1/n)= Θ(n)

练习

在这里插入图片描述

4-1

在这里插入图片描述

4-2

如果分的1-9个桶的值都落在某一个桶中,这样对于该桶进行插入排序,那么总的时间复杂度就为插入排序的时间复杂度Θ(n^2)。如果想用最坏时间为O(nlgn)的算法,应该用比如归并排序来替换插入排序算法,这样目的达成。

4-3

貌似必须抛掷均匀硬币,这样才可能是等可能的出现正背面情况,每次抛掷出现正面的概率是1/2,抛掷2次,那么出现正面次数服从伯努利实验分布,正面算做成功抛掷,其概率是p=1/2,背面算做失败抛掷,其概率是1-1/2,其期望值有公式 E(x)=np 既然抛掷了2次,那么n=2,所以E(x)=2*1/2=1次,假设两次抛掷是独立 E ( x 2 ) = E ( x ) ∗ E ( x ) = E 2 ( x ) = 1 ∗ 1 = 1 E(x^2)=E(x)*E(x)=E^2(x)=1*1=1 E(x2)=E(x)E(x)=E2(x)=11=1这个答案需要满足2个条件(1是均匀硬币,2是两次抛掷独立)

4-4

将圆平均划分为n个区域(相当于n个桶),因为 d i = √ ( X i 2 + Y i 2 ) di=√(Xi^2+Yi^2) di=(Xi2+Yi2)是单位圆里某点到原点的距离(di相当于书中数组中的数,按照桶排序把di分到各个桶中),所以其中di∈(0,1),每个点服从均匀分布,那么每个点等可能的落在这n个区域,所以每个桶中的数据趋近于平均个数,所以这样可以达到平均的Θ(n)时间。
在这里插入图片描述

4-5

(同上)

主要参考

Sorting in Linear Time
算法导论第八章线性时间排序课后答案
算法导论第八章思考题
《算法导论》第三版第8章 线性时间排序 练习&思考题 个人答案

猜你喜欢

转载自blog.csdn.net/y3over/article/details/121336007