您的位置:首页 > 其它

《算法导论》第三版第8章 线性时间排序 练习&思考题 个人答案

2019-01-04 11:25 816 查看

8.1 排序算法的下界

8.1-1

解:n-1。

8.1-2

解:Θ(nlg⁡n)\Theta(n\lg n)Θ(nlgn)。

8.1-3

证明:
n!2≤2n\frac{n!}{2} \le 2^n2n!​≤2n
n!n≤2n\frac{n!}{n} \le 2^nnn!​≤2n
n!2n≤2n⇔n!≤4n\frac{n!}{2^n} \le 2^n \Leftrightarrow n! \le 4^n2nn!​≤2n⇔n!≤4n
对后两种,只能对较小的n存在。

8.1-4

证明:
(k!)n/k≤2h(k!)^{n/k} \le 2^h(k!)n/k≤2h
h≥lg⁡(k!)n/k=(n/k)lg⁡(k!)≥(n/k)(k/2)lg⁡(k/2)(ex 8.1.2)=12nlg⁡k−12n=Ω(nlg⁡k)\begin{aligned} h &\ge \lg(k!)^{n/k} \\ &= (n/k)\lg(k!) \\ &\ge (n/k)(k/2)\lg(k/2) & \text{(ex 8.1.2)}\\ &= \frac{1}{2}n\lg{k} - \frac{1}{2}n \\ &= \Omega(n\lg{k}) \end{aligned}h​≥lg(k!)n/k=(n/k)lg(k!)≥(n/k)(k/2)lg(k/2)=21​nlgk−21​n=Ω(nlgk)​(ex 8.1.2)​

8.2 计数排序

8.2-1

解:中间过程略,最后B是{0, 0, 1, 1, 2, 2, 3, 3, 4, 6, 6},C是{0, 2, 4, 6, 8, 8, 9}

8.2-2

本题和8.2-3证明如下图。

8.2-3

证明如下图。

8.2-4

解:

COUNTING-INTERVAL(A, a, b)
let C[0..k] be a new array
count = 0
for i = 0 to k
C[i] = 0
for j = 1 to A.length
C[A[j]] = C[A[j]] + 1
for i = ceiling(a) to floor(b)
count = count + C[i]
return count

8.3 基数排序

8.3-1

略。。。

8.3-2

插入排序和归并排序是稳定的。对相等的元素记录其原下标,额外开销O(nlg⁡n)O(n\lg n)O(nlgn)时间和O(n)空间。

8.3-3

8.3-4

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

8.3-5

解:d轮;10d10^d10d堆。

8.4 桶排序

8.4-1

略。。。

8.4-2

因为其中运用了插入排序作为子排序算法;可改为归并排序。

8.4-3

解:32\frac{3}{2}23​;111

8.4-4

提示:将did_idi​按照1:2:...:n1:\sqrt2:...:\sqrt n1:2​:...:n​划分为各桶。

8.4-5

思路:我觉得这题就是可以仿照8.4-4,找到一个桶的所有“划分分位”,将元素划分到各个桶间,然后就是类似于桶排序的步骤了。

思考题

8-1 (比较排序的概率下界)

a.


b.

c.


d.

e.


f.

8-2 (线性时间原址排序)

a.

解:计数排序。

b.

解:(仿)快速排序。

c.

解:插入排序。

d.

解:使用计数排序。

e.

解:同d;是稳定的。

8-3 (变长数据项的排序)

a.

思路:可以先对各元素的位数采用计数排序分成各“桶”,再在各“桶”内部采用基数排序。

b.

思路:在右侧补“0”直至与最长字符相齐。

8-4 (水壶)

a.

思路:依次对每一个红(蓝)壶,进行线性查找,找到与之大小相等的蓝(红)壶。

b.

证明思路:可用类似8.1节中的决策树进行证明。

c.

思路:随机找到一红壶→使用此红壶对蓝壶进行PARTITION→使用PARTITION返回的蓝壶(与红壶大小相等)对红壶进行PARTITION→在PARTITION后的前(后)部分中随机找到一红壶→使用此红壶对PARTITION后的前(后)部分蓝壶进行PARTITION→……

8-5 (平均排序)

a.

解:完全排序。

b.

解: 1,3,2,4,5,6,7,8,9,10。

c.

解:
“仅当”的证明:
∑j=ii+k−1A[j]k≤∑j=i+1i+kA[j]k⇕A[i]+∑j=i+1i+k−1A[j]k≤∑j=i+1i+k−1A[j]+A[i+k]k⇕A[i]k≤A[i+k]k⇕A[i]≤A[i+k]\frac{\sum_{j=i}^{i+k-1}A[j]}{k} \le \frac{\sum_{j=i + 1}^{i+k}A[j]}{k} \\ \Updownarrow \\ \frac{A[i] + \sum_{j=i+1}^{i+k-1}A[j]}{k} \le \frac{\sum_{j=i+1}^{i+k-1}A[j] + A[i+k]}{k} \\ \Updownarrow \\ \frac{A[i]}{k} \le \frac{A[i+k]}{k} \\ \Updownarrow \\ A[i] \le A[i+k]k∑j=ii+k−1​A[j]​≤k∑j=i+1i+k​A[j]​⇕kA[i]+∑j=i+1i+k−1​A[j]​≤k∑j=i+1i+k−1​A[j]+A[i+k]​⇕kA[i]​≤kA[i+k]​⇕A[i]≤A[i+k]
反过来可证明“当”。

d.

思路:元素的下标对k取余可分为k组,每组有大概有n/k个数,对每组分别排序即可。

e.

证明思路:结合d与练习6.5-9可证明。

f.

证明思路:结合上面各题易证。

8-6(合并有序列表的下界)

a.

解:C2nnC_{2n}^nC2nn​

b.

证明:
22nπn(1+O(1/n))≤l≤2h\frac{2^{2n}}{\sqrt{\pi n}}(1 + O(1/n)) \le l \le 2^hπn​22n​(1+O(1/n))≤l≤2h
h≥lg⁡(22nπn(1+O(1/n)))=lg⁡22n−lg⁡πn+lg⁡(1+O(1/n))=2n−o(n)\begin{aligned} h & \ge \lg\bigg(\frac{2^{2n}}{\sqrt{\pi n}}(1 + O(1/n))\bigg) \\ & = \lg{2^{2n}} - \lg\sqrt{\pi n} + \lg(1 + O(1/n)) \\ & = 2n - o(n) \end{aligned}h​≥lg(πn​22n​(1+O(1/n)))=lg22n−lgπn​+lg(1+O(1/n))=2n−o(n)​

c.

证明:如果我们不比较两个连续的元素,我们不知道哪个元素首先出现。 与其他元素相比,它们完全无法区分。我们无法确定哪一个应该在前。(注意,如果它们在同一个序列中,我们不需要比较它们,因为我们已经有了这些信息)。

d.

如果元素的排序顺序是⟨a1,b1,a2,b2,…,an,bn⟩\langle a_1, b_1, a_2, b_2, \ldots,a_n, b_n \rangle⟨a1​,b1​,a2​,b2​,…,an​,bn​⟩,我们有两个序列⟨a1,a2,…,an⟩\langle a_1, a_2, \ldots,a_n \rangle⟨a1​,a2​,…,an​⟩和⟨b1,b2,…,bn⟩\langle b_1, b_2, \ldots, b_n \rangle⟨b1​,b2​,…,bn​⟩,那么需要比较2n-1对元素。 处理这种情况的任何算法必须在最坏的情况下执行2n-1比较。

8-7 (0-1排序引理和列排序)

(0-1排序引理部分)

a.

0-1排序引理相关阅读

b.

0-1排序引理相关阅读

(列排序部分)

注:这题我考虑了好久。。。题干中的条件“s必须是r的因子”和“r≥2s2r\geq2s^2r≥2s2”很重要,现在我把算法每一步结束后输出的结果分析一下。

(1) 在第1步后,输出结果:顶部一些全0行,底部一些全1行,中间有一些脏行。

注:脏行个数不限制,可能是0(假设输入的每一列都是由相等比例的0和1组成的),也可能是r(假设输入的一些列是全0,一些列是全1)

(2) 在第2步后,输出结果:{一些全0行→最多1个脏行→一些全1行}→{一些全0行→最多1个脏行→一些全1行}→……(一共s个{}内的内容)

注:第(2)步运用到了条件“s必须是r的因子”,这个条件使得每一列都能转化为整数行,没有哪一行是掺杂了转置前两列的元素。

(3) 在第3步后,输出结果:如d所述。

注:只要理解了第(2)步,这一步很容易理解,脏行最多有s个。

(4) 在第4步后,输出结果:如e所述,且脏列最多只有2个。

注:第3步后存在的最多s脏行,产生了s×s的脏区域。e中所说的“按列读取”其实就相当于在第3步后“按行读取”,肯定会有s2s^2s2个元素组成的脏区域。又因为条件“r≥2s2r\geq2s^2r≥2s2”,脏列肯定不会超过2个。

(5) 在第5步后,输出结果:对第4步中可能存在的脏列进行排序。

注:如果第4步后脏列只有1个,其实在此时排序已完成;如果第4步后脏列有2个,则2个脏列必然相邻,且每个脏列的脏区不会超过一列的一半(因为条件“r≥2s2r\geq2s^2r≥2s2”;且前一脏列的脏区在后半部分,后一脏列的脏区在前半部分)。
但需要注意的是,此时排序并不一定完成,因为虽然对每一脏列进行了排序,但如果有2个脏列的话,前一脏列的末尾处的1和后一脏列开始处的0,拼接后,显然还是不符合排序的原则的。

(6) 在第6步和第7步后,输出结果:此时按列读取的数组已经是排好序的数组了。

注:这两步其实就是对“第4步后脏列有2个”的情况进行处理,将前一脏列的后半部分与后一脏列的前半部分组合成一个新列(因为每个脏列的脏区不会超过一列的一半,所以实际上数组中剩下所有的脏区域都在此时集中到新列里),再进行排序,这样实际上数组的排序已经完成(因为按列读取的话,新列就是数组唯一的脏区域,它前面只有0,后面只有1)。

(7) 在第8步后,输出结果就是最终输出结果。

c.

由于偶数步骤盲目地执行,我们可以怀疑算法中有一些遗忘的元素。如果我们用一个不经意的比较交换算法执行奇数步骤,那么列排序显然是遗忘的,我们可以应用0-1排序引理。由于我们可以将这些步骤视为“黑盒子”,因此我们可以将排序算法替换为产生相同结果的任何其他算法(即任何排序算法),并且生成的列排序仍然会排序。

d.

d、e、f可由上面的分析得出。

e.

d、e、f可由上面的分析得出。

f.

d、e、f可由上面的分析得出。

g.

思路:因为没有了条件“s必须是r的因子”,所以每一列都无法转化为整数行,因此必然有一些行掺杂了转置前两列的元素。
解:
现在第2步的输出结果变为{一些全0行→最多1个脏行→一些全1行→一些脏行}→{一些全0行→最多1个脏行→一些全1行→一些脏行}→……(一共s-1个{}内的内容)→一些全0行→最多1个脏行→一些全1行。
在{}中,第一个脏行是之前列内部存在的,而第二个脏行是之前两列元素掺杂形成的,因此第一个脏行共有最多s个,第二个脏行共有最多s-1个(最差情况下,每相邻两列的元素都会掺杂)。因此最多有2s-1个脏行,而第3步不会增加新的脏行,所以第3步后中间最多2s-1行脏行。
4s2−2s4s^2-2s4s2−2s

h.

思路:两列元素掺杂形成脏区,必然是前一列的末尾是1,后一列的开始是0,改动后使得前后两列的衔接处元素相同即可。
解:奇数列升序排序,偶数列降序排序。这样可以使脏行数最多为s。
注:其实并不一定是衔接处的脏行被削除了,也有可能转置前前一列是全1,后一列是全0,这样转置后衔接处仍有脏行,但这样的话原列的内部必然没有脏区,所以列内部转置产生的脏行就没有了。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: