Problem 7-1
a.我们将使用参数p = 1, r = | A | = 12进行调用。所以x = 13。
我们确实看到分割已经移动了两个比枢轴大的元素,19和21,到数组的最后两个位置。
b.我们知道,在循环的开始,我们有i < j,因为它最初是成立的,只要|A|≥2。如果在某个迭代中它是不真实的,那么我们就会在之前的迭代中离开循环。为了证明我们不会访问数组外的元素,我们需要证明在每次循环开始时,有一个k > i使得 A[k]≥x,并且有一个k'< j使得A [j']≤x。这显然是正确的,因为最初i和j在数组的边界之外,所以元素x必须在两者之间。由于i < j,我们可以选择k = j和k' = i。元素k满足与x的期望关系,因为位置j的元素是在第12行进行交换之前循环的前一次迭代中位置i的元素。同理,对于k'。
c.如果主循环运行不止一次,则j < r,因为它在每次迭代中至少减少1。
注意,在循环第一次运行的第11行,我们有i = 1,因为A[p] = x≥x。因此,如果我们要在主循环的一次迭代后终止,我们也必须有j = 1 < p。d.我们将展示循环不变量A[p..i]小于或等于x, x小于或等于A[j…r]中的所有元素。在第一次迭代之前,因为这两组元素都是空的,所以基本上是正确的。假设我们刚刚完成了循环的一次迭代,其中j从j1到j2, i从i1到i2。A中的所有元素[i1+1…i2−1]< x,因为它们不会导致第8−10行上的循环终止。类似地,我们有A[j2 + 1...j1 - 1]中的所有元素均> x。在第12行交换后,我们还得到A[i2]≤x≤A[j2]。最后,通过归纳法,我们得到A[p...i1]中的所有元素小于等于x,且A[j1...r]中的所有元素大于或等于x,那么,把它们放在一起,因为A[p..i2] = A[p…i1]∪A[i1 + 1…i2-1]∪A[i2]}和A[j2..r] =∪{A[j2]}∪A[j2+1..j1 - 1]∪A[j1..r],我们得到了期望的不等式。由于在终止时,我们有i≥j,我们知道A[p..j] ⊆ A[p..i],因此,A的每个元素[p…j]小于等于x, x小于等于A[j + 1..r] ⊆ A[j..r]。
e.在运行Hoare-partition之后,我们不能保证枢轴值会在位置j,所以,我们将扫描列表找到枢轴值,把它放在两个子数组之间,然后递归。
Problem 7-2
a.由于所有元素都是相同的,索引和交换的初始随机选择没有改变任何内容。因此,随机化快速排序的运行时间与快速排序的运行时间相同。因为所有元素都是相等的,所以PARTITION(A, P, r)总是返回r - 1。这是最坏情况下的分区,所以运行时间是Θ(n^2)。
b.参见PARTITION算法进行修改。
c.参见RANDOMIZED-PARTITION算法进行修改。
d.设d为A中不同元素的个数。运行时间主要由在PARTITION过程中花费的时间决定,并且最多可以调用d个PARTITION。如果X是在整个QUICKSORT的执行过程中,在PARTITION的第4行执行的比较次数,那么运行时间是O(d + X)。每对元素最多只比较一次。如果zi是第i个最小的元素,我们需要计算zi相对于zj的概率。这一次,一旦选择了一个主点x且zi≤x≤zj,我们就知道zi和zj不能在以后的任何时间进行比较。这就是分析的不同之处,因为数组中可能有许多元素等于zi或zj,所以比较zi和zj的概率降低了。然而,随机数组中不同元素的期望百分比趋向于1 - 1/e,因此渐近地期望比较次数是相同的。
Problem 7-3
a.由于枢轴是作为数组中大小为n的随机元素选择的,因此任何特定元素被选中的概率都是相等的,并且加上1,因此都是1/n。 因此,E[Xi] = Pr[最小的i被选中]= 1/n。
b.我们可以对所有事件Xi应用线性期望。假设我们有一个特定的Xi为真,那么,我们将有一个长度为i - 1的子数组,另一个长度为n - i,当然仍然需要线性时间来运行划分过程。这与式(7.5)中的和式完全对应。
c.
d.我们将用不同于提示的方法来证明这个不等式。如果让f(k) = klg (k)作为连续函数,那么f'(k) = lg(k)+ 1。注意这个求和是f(k)从2到n的积分的左边逼近步长为1。通过分部积分,klgk的不定积分是
因此,代入积分限,相减,得到
。
因为f在整个积分区间内的导数是正的,左手定则提供了积分的欠逼近,所以,我们有这个最后一个不等式使用了ln(2) > 1/2这个事实。
e.通过归纳假设T(q)≤q lg(q) + Θ(n)。结合式(7.6)和式(7.7),我们有![]()
Problem 7-4
a.我们将采用归纳法进行。对于基本情况,如果A包含1个元素,则p = r,因此算法立即终止,只留下一个排序元素。现在假设对于1≤k≤n−1,尾部递归快速排序正确排序一个包含k个元素的数组A。设A的大小为n,我们设q等于主节点根据归纳假设,TAIL-RECURSIVE-QUICKSORT会正确排序大小严格小于它的左子数组。接下来,将p更新为q+1,接下来的步骤序列与我们最初调用TAIL-RECURSIVE-QUICKSORT(A,q+1,n)完全相同。同样,这个数组的大小严格较小,因此根据归纳假设,它正确地排序了A[q + 1…n]如所愿。
b.如果输入数组已经排序,则堆栈深度为Θ(n)。右子数组的大小始终为0,因此在违反while条件p < r之前将有n - 1次递归调用。
c.我们修改算法,在较小的子数组上进行递归调用,以避免在堆栈上构建过多的push:
Problem 7-5
a. pi是随机选择大小为3的子集以A'[i]作为中间元素的概率。所选的三个元素有6种可能的排序。假设S'是三个选定元素的集合。我们将计算在所有可能的3-集合中,S'的第二个元素为A'[i]的概率,因为每个3-集合对应着6个有序的3-集合,所以这些概率是相等的。我们将计算S'[2] = A'[i]的概率。对于任何这样的S',我们需要从[i−1]中选择第一个元素,从{i + 1,…n}。所以有(i−1)(n−i)个这样的3-集合。3-集合的总数是
所以,
b.如果我们让
,之前的结果会让我们增加
当n趋于无穷时,我们得到
。
c.为了避免混乱,假设n是3的倍数。我们将求和近似为积分,所以,
在n→∞的极限下,它等于13/27这个常数大于1/3在最初的随机化快速排序实现中。
d.由于新算法总是有一个“坏”的选择,这个选择在原始快速排序的常数因子范围内,它仍然有一个合理的概率,随机导致我们进入一个糟糕的情况,所以,它仍然是nlgn。
a.我们的算法本质上与问题2中修改后的随机化快速排序相同。我们对这些ai进行排序,但是我们替换比较运算符来检查重叠区间。唯一需要的修改是在PARTITION中进行的,所以我们将在这里重写它。对于位置i中的给定元素x,我们将分别使用x.a和x.b来表示ai和bi。
当区间重叠时,我们将它们视为相等的元素,从而减少了排序所需的时间。
b.对于不同的时间间隔,算法的运行方式与常规快速排序完全相同,因此其预期运行时间通常为Θ(nlgn)。如果所有的间隔重叠,那么第12行的条件将满足for循环的每次迭代。因此算法返回p和r,因此只剩下空数组待排序。FUZZY-PARTITION只会被调用一次,并且由于它的运行时仍然是Θ(n),所以总的预期运行时是Θ(n)。