算法导论第八章思考题参考答案(15)

Problem 8-1

a.有n!个输入数组的可能排列,因为输入元素都不同。因为每一个都是等可能的,所以分布在这个集合上是一致支持的。所以,每一个出现的概率都是1/n!,并且对应于不同的叶结点,因为程序需要能够区分它们。

b. LT中特定元素的深度(例如:RT)在考虑T的元素时,都比它们的深度小1。特别是,对于两个子树的叶子是成立的。同样,{LT, RT}构成了T的所有叶节点的划分,因此,如果我们让L(T)表示T的叶节点,

c.假设我们有一个T有k个叶结点,使得D(T) = d(k)。设i0为LT中的叶节点个数,则d(k) = D(T) = D(LT) + D(RT) + k,但我们可以选择LT和RT使外部路径长度最小。

d.我们把i当作一个连续变量,求导来找到临界点。给定的表达式对i求导如下

当我们有,此表达式为0。因此,(1+e^{-2})i=k,因此,i=\frac{k}{1+e^{-2}}.因为我们选择的两个子树大小大致相等,总深度将是lg(k)阶,每一层贡献k,所以总外部路径长度至少是k lg(k)。

e.因为之前我们说过,一棵有k个叶子的树的外部长度必须是klg (k),而一棵排序树至少需要n!树,一个排序树的外部树长度必须至少为n!lg (n!)。因为平均情况下的运行时间是叶节点的深度乘以该叶节点出现的概率,所以我们知道运行时间至少是 n!lg (n!)/ n!= lg(n!)∈Ω(n lg(n))。

f.由于预期的运行时间是随机位的所有可能结果的平均值,如果每个可能的随机性修复导致更高的运行时间,平均值也必须更高。 

 Problem 8-2

a.

该算法首先选择所有键为0的元素,按照它们在A中出现的顺序放入新数组C中,然后选择所有键为1的元素,按照它们在A中出现的顺序放入C中,因此它是稳定的。因为它只遍历数组两次,所以它是O(n)。

该算法维持到目前为止在数组开头看到的那些0。每次遇到键值为0的元素时,算法将其与已看到的最后一个0后面的位置交换。这是原地的O(n)但不稳定。

b.

c.在数组上运行BubbleSort。

d.使用a部分给出的算法。对于每个b位密钥,它需要O(n),并且是稳定的,这是基数排序所要求的。因此,总运行时间为O(bn)。

e.按照计数排序的第1行到第5行创建数组C。创建一个数组B,它是C的副本,然后在B上运行第7行到第9行。换句话说,C[i]给出了a中等于i的元素的数量,B[i]给出了a中小于或等于i的元素的数量,所以给定一个元素,我们可以正确地识别它在数组中的位置。当第i位的元素不属于这里时,将它与它所属位置的元素交换。一旦出现属于位置i的元素,则增加i。预处理需要O(n + k)时间,交换的总次数最多为n,并且我们遍历整个数组一次,因此总体运行时间为O(n + k)。该算法具有就地排序的优点,但是它不再像计数排序那样稳定。

Problem 8-3 

a.首先,按整数长度排序。这可以使用桶排序来有效地完成,我们为每个可能的位数创建一个桶。我们用基数排序对这些等长整数集进行排序。然后,我们将从每个桶中获得的排序列表连接起来。

b.为字母表中的每个字母做一个桶,每个桶包含以该字母开头的单词。然后,忘记桶中每个单词的第一个字母,将空单词(如果它在这个新的单词集合中)与递归到这些长度小于1的单词的结果连接起来。因为每个单词被处理的次数等于它的长度,所以运行时间在字母总数上是线性的。

Problem 8-4 

a.选择一个红色的罐子。把它和蓝色的罐子比较,直到你找到一个匹配的。把那一对放在一边,重复做下一个红色罐子。这最多使用次比较。

b.我们可以想象首先把红色的罐子按某种顺序排列。那么这个问题的解决方案就变成了蓝罐的排列使得第i个蓝罐和第i个红罐的大小相同。如8.1节所述,我们可以制作一个决策树来表示蓝罐和红罐之间的比较。内部节点表示一对特定的红罐和蓝罐之间的比较,叶子节点表示基于比较结果的蓝罐的排列。我们感兴趣的是当一个罐大于,小于,或者等于另一个罐的大小,所以树应该有3个子节点每个节点。因为至少有n个!叶节点,决策树的高度必须至少为log3(n!)由于解对应于从根到叶的简单路径,因此算法必须至少进行Ω(nlgn)次比较才能到达任何叶节点。 

c.我们使用一种类似于随机化快速排序的算法。随机选择一个蓝色的罐子。把红罐子分成比蓝罐子小的和大的两部分。在比较的某个点上,你会发现红色的罐子大小相同。一旦红色的罐子被分成大小,用同样大小的红色罐子把蓝色的罐子分成小的和大的。如果k个红色的罐子比原来选择的罐子小,我们需要在大小为k−1和n−k的输入上解决原来的问题,我们将以同样的方式做。大小为1的子问题很容易解决,因为如果只有一个红色罐和一个蓝色罐,它们必须是相同的大小。对预期比较次数的分析与第181-184页给出的随机快速排序完全相同。我们运行这个过程两次,所以预期的比较次数是两倍,但是这被大O符号吸收了。在最坏的情况下,我们每次都选择最大的罐子,这将导致次比较。

Problem 8-5 

a.由于每个平均值都是单个元素的集合,因此,每个元素都小于下一个元素。这就是排序的一般定义。

b. <1, 6, 2, 7, 3, 8, 4, 9, 10>

c.假设对于所有合适的i, A[i]≤A[i+k],则中的每个元素在中都有一个对应的小于它的元素。这意味着和必须具有期望的不等式。现在,假设这个数组是k排序的。这意味着,但是,如果我们减去这两个和式共有的所有项,我们得到: A[i] ≤ A[i + k]

d.我们可以做快速排序,直到子数组的大小最多为k。在练习7.4-5中,这需要花费O(nlg (n/k))的时间。注意,这是所有需要做的工作,因为我们只需要证明以k个位置分隔的元素位于不同的块中,这是真的,因为它们的宽度≤k个位置。

e.考虑这些列表,对于每一个i∈{0,…,k−1},则Li = < A[i], A[i +k],…A[i+\left \lfloor n/k \right \rfloor k] >。注意,根据c部分,如果A是k−排序的,那么我们必须让每个Li都排序。然后,通过6.5-9,我们可以在期望的时间内将它们合并成一个有序的数组。

f.设t(k, n)是对长度为n的数组进行k次排序所需的时间。然后,对于每个k,我们可以先对数组进行k次排序,然后在时间t(n, k) + nlgk中应用前一部分。如果t(n, k)等于o(nlgn),那么我们就会与基于比较的排序下界相矛盾。

Problem 8-6 

a.有\binom{2n}{n}种方法将2n个数字分成两个排序列表,每个列表有n个数字。 

b.任何要合并两个排序列表的决策树必须至少有\binom{2n}{n}叶节点,因此它的高度至少为lg\binom{2n!}{n!n!}。我们将使用练习8.1-2中导出的不等式来约束它:

c.如果我们不比较它们,那么就没有办法区分原始的未合并列表和通过在列表之间交换这两个项而获得的未合并列表。在两个元素不同的情况下,为了正确合并列表,我们需要采取不同的操作,因此必须比较这两个元素。 

d.让列表A = 1,3,5,…,2n−1和B = 2,4,6,…, 2n。在c部分,我们必须比较1和2,2和3,3和4,以此类推,直到我们比较2n - 1和2n。这相当于总共进行2n−1次比较。

Problem 8-7 

a.因为我们声称A[p]是放置在错误位置的最小值,所以所有值小于或等于A[p]的东西都在它的正确位置,因此,不能放在A[p]应该在的位置。这意味着A[q]≥A[p]。同样,如果我们有A[q] = A[p]。那么在应该有A[q]的位置上有A[q]就不是错误了。得到A[q] > A[p]所以1 = B[q]。因为相等使我们在0的情况下,我们有0 = B[p]。 

b.设Ai为前i次操作后的A, Bi为前i次操作后的B。A = A0 ,B = B0。对于所有的j和i,我们可以保持如下的不变量

最初(在i = 0时)它是成立的,因为我们定义了那些比A[p]大的元素在B中的对应位置为1,同样,那些比A[p]小的元素在B中的对应位置为0。现在,假设在第i + 1步,我们在j1和j2的位置之间执行一些比较交换操作,j1 < j2。通过归纳,Ai[j1]≤Ai[j2]而Bi[j1] > Bi[j2]是不可能的,因为B中的这种关系意味着Ai[j1] >A[p]≥Ai[j2],这与我们对A的假设相矛盾。

这就留给我们四种情况:

情况1:Ai[j1]≤Ai[j2], Bi[j1]≤Bi[j2]。然后没有交换发生在两个数组中,即Ai+1 = Ai和Bi+1 = Bi。显然,期望的属性继续保持不变。
案例2:Ai[j1] > Ai[j2]且Bi[j1]≤Bi[j2]。然后,交换将发生在a而不是b中。我们还知道Ai[j1]和Ai[j2]都大于A[p],或者两者都大于A[p]。否则我们会有Bi[j1] = Bi[j2],这意味着0 = Bi[j1] < Bi[j2] = 1,这意味着Ai[j1]≤A[p] < Ai[j2],这与我们所处的情况相矛盾。假设我们在Ai[j1]和Ai[j2]都大于A[p]的情况下,这两个较小的情况将是相同的,除了B数组中的位置等于0而不是1。由于两者都较大,我们有Bi[j1] = Bi[j2] = 1,这意味着Ai+1[j2] = Ai[j1] > A[p]和Ai+1[j2] = Ai[j1] > A[p]。

案例3:Ai[j1] > Ai[j2]和Bi[j1] > Bi[j2]。交换发生在A和B中。如果j既不是j1也不是j2,它是不变的,所以我们不需要考虑它。对于j2,我们有

j1的情况几乎相同。补充证明(*)。

把它们放在一起,设i是操作的总数,设j是A[p]最终的位置,k是它应该结束的位置。我们知道k < j因为A[p]是最小的错误排序元素。如果它的位置比它应该的位置低,它就会占据属于较小值的位置。如果它的位置应该属于一个相等的值,那么它就不会被错误排序。根据定义A[q] = Ai[k], A[p] = Ai[j]。根据a部分,我们有Ai[j] > A[p]。通过我们刚刚证明的不变量,Bi[j] > 0, B[k] =0。因此,B没有排序。

c.算法中所有的偶数步都不去看单元格的值,它们只是把它们推来推去。奇数步也是无关的因为我们可以使用插入排序的无关比较交换版本在问题开始时给出。

d.在第一步之后,我们知道每一列看起来像一些0后面跟着一些1。假设列i有zi个0。然后,在重塑步骤之后,我们知道每一列将贡献\left \lceil z_{i}/(r/s) \right \rceil个0到前(zi模s)列,其余的少一个。特别是,由于在第2步之后,每个列的0的数量只会跳跃1,因此每个列的和最多只能改变s。然后,排序之后,这意味着只有s个脏行。

e.从前半部分,我们知道在步骤4之前,最多有s个脏行,其中总共有s^2个元素。在第4步之后,这些脏的元素将映射到一列的某个底部,另一列的某个部分,然后是一列的某个顶部。左边的都是0,右边的都是1。

f.从上一部分,我们知道最多有s^2个值可能是坏的。r≥2* s^2。有两种情况。首先,脏区域横跨两列,在这种情况下,我们在步骤6之后,脏区域完全在一列中。因此,在执行第7步之后,我们得到脏的列有一个干净的上半部分,并且数组已经排序,因此,在应用第8步时它仍然是排序的。第二种情况是脏区域完全在一列中。如果是这种情况,那么,在第5步排序之后,数组已经排序,并且唯一的脏列具有干净的前半部分。因此,在执行步骤6-8之后,它将保持它被排序。

g.这个证明与e部分的证明方法非常相似。必须注意步骤2和步骤4中的转换是如何更改的。我们需要,因为一个2s−1行的脏区对应一个的脏区(当读取主要列时)。我们可以简单地用一些全为零的行填充数组,使行数达到s的倍数。然后,从最终答案中去掉这些零。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值