2-1
(在归并排序中队小数组采用插入排序) 虽然归并排序的最坏情况运行时间为Θ(n2)\Theta(n^2)Θ(n2),但是插入排序中的常量因子可能使得它在nnn较小时,在许多机器上实际运行得更快。因此,在归并排序中当子问题变得足够小时,采用插入排序使得递归的叶变粗是有意义的。考虑对归并排序的一种修改,其中使用插入排序来排序长度为kkk的n/kn/kn/k个子表,然后使用标准的合并机制来合并这些子表,这里kkk是一个特定的值。
- 证明:插入排序最坏情况可以在Θ(nk)\Theta(nk)Θ(nk)时间内排序每个长度为kkk的n/kn/kn/k个子表。
- 表明在最坏情况下如何在Θ(nlog2(n/k))\Theta(nlog_2(n/k))Θ(nlog2(n/k))时间内合并这些子表。
- 假定修改后的算法的最坏情况运行时间为Θ(nk+nlog2(n/k))\Theta(nk+nlog_2(n/k))Θ(nk+nlog2(n/k)),要使修改后的算法与标准的归并排序有相同的运行时间,作为nnn的一个函数,借助Θ\ThetaΘ记号,kkk的最大值是多少?
- 在实践中,我们应该如何选择kkk?
答:
- 在最坏情况下,使用插入排序排序一个长度为kkk的列表的运行时间是Θ(k2)\Theta(k^2)Θ(k2)。因此,在最坏情况下,排序n/kn/kn/k个子列表(每个子列表的长度为kkk)的运行时间是Θ(k2⋅n/k)=Θ(nk)\Theta(k^2 \cdot n/k)=\Theta(nk)Θ(k2⋅n/k)=Θ(nk)。
- 我们有n/kn/kn/k个子列表每一个子列表的长度是kkk。为了合并这n/kn/kn/k个已排序的子列表成为一个长度为nnn的单一已排序的列表,我们必须每次合并2个子列表。如果两两为一组的子列表合并完了,就继续再两两一组。这需要花费我们log2(n/k)log_2(n/k)log2(n/k)步,并且每一步需要比较nnn个元素。所以,在最坏情况下,合并这些子列表所花费的时间是Θ(n⋅log2(nk))\Theta(n \cdot log_2(\frac{n}{k}))Θ(n⋅log2(kn))。
- 当Θ(nk+n⋅log2(nk))=Θ(n⋅log2n)\Theta(nk+n\cdot log_2(\frac{n}{k})) = \Theta(n\cdot log_2n)Θ(nk+n⋅log2(kn))=Θ(n⋅log2n)时,修改后的算法具有普通合并排序的算法复杂度。假定k=Θ(log2n)k = \Theta(log_2n)k=Θ(log2n),
Θ(nk+n⋅log2(nk))=Θ(nk+n⋅log2n−n⋅log2k)\Theta(nk + n\cdot log_2(\frac{n}{k})) = \Theta(nk + n\cdot log_2n - n\cdot log_2k)Θ(nk+n⋅log2(kn))=Θ(nk+n⋅log2n−n⋅log2k)
Θ(n⋅log2n+n⋅log2n−n⋅log2(log2n))\Theta(n\cdot log_2n + n\cdot log_2n - n\cdot log_2(log_2n))Θ(n⋅log2n+n⋅log2n−n⋅log2(log2n))
=Θ(2n⋅log2n−n⋅log2(log2n))=\Theta(2n\cdot log_2n - n\cdot log_2(log_2n))=Θ(2n⋅log2n−n⋅log2(log2n))
=Θ(n⋅log2n)=\Theta(n\cdot log_2n)=Θ(n⋅log2n)。 - 将kkk选定为当插入排序比合并排序更快时,子列表的最大长度。
2-2
(冒泡排序的正确性)冒泡排序是一种流行但低效的排序算法,它的作用是反复交换相邻的未按次序排列的元素。
BUBBLESORT(A)
for i = 1 to A.length - 1
for j = A.length downto i + 1
if A[j] < A[j - 1]
exchange A[j] with A[j - 1]
- 假设A′A'A′表示BUBBLESORT(A)的输出。为了证明BUBBLESORT正确,我们必须证明它将终止并且有:
A′[1]⩽A′[2]⩽A′[3]⩽⋯⩽A′[n]A'[1]\leqslant A'[2]\leqslant A'[3]\leqslant \cdots\leqslant A'[n]A′[1]⩽A′[2]⩽A′[3]⩽⋯⩽A′[n]
其中n=A.lengthn = A.lengthn=A.length。为了证明BUBBLESORT确实完成了排序,我们还需要证明什么?
下面两部分将证明不等式。 - 为第2~4行的for循环精确地说明一个循环不变式,并证明该循环不变式成立。你地证明应该使用本章给出地循环不变式证明地结构。
- 使用(2)部分证明的循环不变式的终止条件,为第1~4行的for循环说明一个循环不变式,该不变式将使你能证明不等式。你的证明应该使用本章给出的循环不变式证明的结构。
- 冒泡排序的最坏情况运行时间是多少?与插入排序的运行时间相比,其性能如何?
答:
- A′A'A′是由AAA中的元素组成,且是按顺序排列的。
- 证明过程需要完善
- 循环不变式为第1-4行的for循环在第iii次循环之前,A[1..i−1]A[1..i - 1]A[1..i−1]已排好序,且A[1]A[1]A[1]为数组AAA最小的元素,数组A[2]A[2]A[2]为次小的元素,等等。数组AAA剩余的元素A[i..A.length]A[i..A.length]A[i..A.length]即为未排序的元素。初始化:在第1次循环之前,iii的值是1,数组A[1..i−1]A[1..i-1]A[1..i−1]为空,因为在初始化的时候所有元素均未排序,故循环不变式成立。保持:在第iii次循环开始之前,数组A[1..i−1]A[1..i-1]A[1..i−1]中的依次从小到大排列,且是数组AAA中最小的i−1i-1i−1个元素。在第iii次循环中,数组A[i+1..A.length]A[i + 1..A.length]A[i+1..A.length]中最小的元素会逐步冒泡“上升”到数组AAA的第iii个位置。所以,当前循环结束后,第iii个位置的位置即为数组AAA中“第iii小”的元素。因此,循环不变式保持成立。终止:循环终止条件为i==A.lengthi == A.lengthi==A.length,此时A[1..i−1]A[1..i-1]A[1..i−1]中的元素即为数组AAA中的所有元素除最大的元素之外的所有元素,且已按从小到大的顺序排列,因A[A.length]A[A.length]A[A.length]就是数组AAA中的仅剩的元素,也是最大元素,所以数组AAA中的所有元素均已被排序。
- 冒泡排序的结果是数组AAA中的元素以从小到大的顺序排列。最坏情况是,原始情况下数组AAA以从大到小的顺序排列。这个时候,对于第2行的for循环的每次迭代,第3行的的判断均成立,故第4行的交换均执行。所以,对于第1行的for循环的iii,第4行的执行次数为A.length−iA.length - iA.length−i,假设交换数组元素所需的运行时间为c,则冒泡排序总共所需运行时间为∑1n−1c(n−i)=cn(n−1)2\sum _{1}^{n-1}c(n - i) = \frac{cn(n-1)}{2}∑1n−1c(n−i)=2cn(n−1)。故,冒泡排序的最坏情况运行时间是Θ(n2)\Theta(n^2)Θ(n2),与插入排序相比,所需的运行时间是一样的。
2-3
(霍纳(Horner)规则的正确性)给定系数a0,a1,⋯,ana_0,a_1,\cdots,a_na0,a1,⋯,an和xxx的值,代码片段
y = 0
for i = n downto 0
y = ai + x*y
实现了用于求职多项式
P(x)=∑k=0nakxk=a+0+x(a1+x(a2+⋯+x(an−1+xan)⋯ ))P(x) = \sum_{k = 0}^{n}a_kx^k = a+0 + x(a_1 + x(a_2 + \cdots + x(a_{n-1} + xa_n)\cdots))P(x)=k=0∑nakxk=a+0+x(a1+x(a2+⋯+x(an−1+xan)⋯))
的霍纳规则。
- 借助Θ\ThetaΘ记号,实现霍纳规则的以上代码片段的运行时间是多少?
- 编写伪代码来实现朴素的多项式求值算法,该算法从头开始计算多项式的每个项。该算法的运行时间是多少?与霍纳规则相比,其性能如何?
- 考虑以下循环不变式:
在第2~3行for循环每次迭代的开始有
y=∑k=0n−(i+1)ak+i+1xky = \sum_{k = 0}^{n-(i + 1)}a_{k + i + 1}x^ky=k=0∑n−(i+1)ak+i+1xk
把没有项的和式解释为等于0。准找本章中给出的循环不变式证明的结构,使用该循环不变式来证明终止时有y=∑k=0nakxky = \sum_{k = 0}^{n}a_kx^ky=∑k=0nakxk。 - 最后证明上面给出的代码片段将正确地求由系数a0,a1,⋯,ana_0,a_1,\cdots,a_na0,a1,⋯,an刻画地多项式的值。
答:
- 实现霍纳规则的以上代码片段的运行时间是Θ(n)\Theta(n)Θ(n)。
- 朴素的多项式求值算法:
sum = 0
pow = 1
for 0 downto n
sum += ai * pow
pow *= x
该算法的运行时间是Θ(n)\Theta(n)Θ(n)。该算法的性能霍纳规则是一样的。
-
第2~3行for循环不变式为每次迭代开始之前y=∑k=0n−(i+1)ak+i+1xky = \sum_{k = 0}^{n - (i + 1)}a_{k + i + 1}x^ky=∑k=0n−(i+1)ak+i+1xk。初始化:当第一次循环之前,和式中没有项,故y = 0。保持:在第iii次迭代之后,我们有
y=ai+x∑k=0n−(i+1)xky = a_i + x\sum_{k = 0}^{n - (i + 1)}x^ky=ai+xk=0∑n−(i+1)xk
=aix0+x∑k=0n−i−1ak+i+1xk+1 = a_ix^0 + x\sum_{k = 0}^{n - i - 1}a_{k + i + 1}x^{k + 1}=aix0+xk=0∑n−i−1ak+i+1xk+1
=aix0+∑k=1n−iak+ixk = a_ix^0 + \sum_{k = 1}{n - i}a_{k + i}x^k=aix0+k=1∑n−iak+ixk
=∑k=0n−iak+ixk = \sum_{k = 0}^{n - i}a_{k + i}x^k=k=0∑n−iak+ixk
终止:当i=−1i = -1i=−1时,循环结束。此时
y=∑k=0n−i−1ak+i+1xk=∑k=0nakxky = \sum_{k = 0}^{n - i - 1}a_{k + i + 1}x^k = \sum_{k = 0}^na_kx^ky=k=0∑n−i−1ak+i+1xk=k=0∑nakxk。 -
循环的不变量是一个等于一个具有给定系数的多项式的和。
2-4
(逆序对)假设A[1..n]A[1..n]A[1..n]时一个有nnn个不同数的数组。若i<ji<ji<j且A[i]>A[j]A[i]>A[j]A[i]>A[j],则对偶(i,j)(i, j)(i,j)称为AAA的一个逆序对(inversion)。
- 列出数组<2,3,8,6,1><2, 3, 8, 6, 1><2,3,8,6,1>的5个逆序对。
- 有集合1,2,⋯ ,n{1, 2, \cdots, n}1,2,⋯,n中的元素构成的什么数组具有最多的逆序对?她有多少逆序对?
- 插入排序的运行时间与输入数组中逆序对的数量之间是什么关系?证明你的回答。
- 给出一个确定在nnn个元素的任何排列中逆序对数量的算法,最坏情况需要Θ(n⋅log2n)\Theta(n\cdot log_2n)Θ(n⋅log2n)时间。(提示:修改该归并算法。)
答:
- <1,5>、<2,5>、<3,4>、<3,5>、<4,5><1, 5>、<2, 5>、<3, 4>、<3, 5>、<4, 5><1,5>、<2,5>、<3,4>、<3,5>、<4,5>。
- 如果集合1,2,⋯ ,n{1, 2, \cdots, n}1,2,⋯,n中的元素是以降序排列的,那么组成的逆序对最多。它有n(n−1)2\frac{n(n-1)}{2}2n(n−1)个逆序对。
- 如果输入数组中的逆序对数量越多,则插入排序的运行时间就越大。对于数组中的的逆序对<i,j><i, j><i,j>,在应用插入排序的时候,为了保证数组按正序排列,必定会交换逆序对<i,j><i, j><i,j>。每一个逆序对会花费时间ttt,那么在排序数组的时候,总共花费的时间就是逆序对的数目m⋅tm\cdot tm⋅t。所以,插入排序的运行时间与输入中逆序对的数量之间呈正比关系。
INVERSION-MERGE(A, p, q, r)
result = 0
for m = p to to q
for n = q + 1 to r
if A[m] < A[n]
break
result++
n1 = q - p + 1
n2 = r - q
let L[1 .. n1] and R[1 .. n2] be new arrays
for i = 1 to n1
L[i] = A[p + i - 1]
for j = 1 to n2
R[j] = A[q + j]
i = 1
j = 1
for k = p to r
if L[i] <= R[j]
A[k] = L[i]
i = i + 1
else
A[k] = R[j]
j = j + 1
return result
INVERSION-SORT(A, p, r)
result = 0
if p < r
q = (p + r) / 2
result += INVERSION-SORT(A, p, q)
result += INVERSION-SOFT(A, q + 1, r)
result += INVERSION - MERGE(A, p, q, r)
return result
本文探讨了归并排序中结合插入排序优化的策略,分析了在不同子列表长度kkk下,排序与合并操作的时间复杂度。通过证明,展示了在最坏情况下,算法如何在Θ(nk+nlog2(n/k))时间内完成,同时讨论了kkk的最佳取值以匹配标准归并排序的时间复杂度。此外,还对比了冒泡排序的正确性和效率,以及霍纳规则在多项式求值中的优势。
1552

被折叠的 条评论
为什么被折叠?



