《算法导论》第二章习题解答
如果错误,或者有更好的方法,欢迎大家指正
2-1:插入排序
2-1-1:描述数组A = {31,41,59,26,41,58}插入排序过程
解: 1、 31 41 59 26 41 58
2、 31 41 59 26 41 58
3、 26 31 41 59 41 58
4、 26 31 41 41 59 58
5、 26 31 41 41 58 59
2-1-2:写出插入排序(非升序)伪代码
解: INSERTION_SORT(A)
for i <- 2 upto length(A)
key <- A[i]
j <- i-1
while A[j] < key and j>0
A[j+1] <- A[j]
i <- i-1
A[i+1] <- key
endfor
2-1-3:考虑下面的查找问题
输入:一列数A=<a1,a2,….,an>和一个值v
输出:下标i,使得v = A[i],如果v不在A中,输出NIL
写出针对这个问题的线性查找伪代码,它顺序地扫描整个序列以查找v,利用循环不变式说明正确性
解: FindValue(A[1…..n],v)
for i <-1 upto n
if A[i] = v then return i
endfor
return NIL
初始化: 在迭代第一轮之前,是正确的,因为它没有进行任何查找工作,也没有返回任何数值
保持: 假设在第k次迭代是正确的,那么有两种情况,在k的时候算法已经结束了,可知返回的结果是正确的,如果没有返回那么说明前面k次没有 匹配到v,那么k+1次迭代的时候也是正确的
终止: 有两种情况,在中途终止,即找到了i,在最后终止,没有找到i,返回NIL
2-1-4: 有两个各存放在数组A和B的n位二进整数,考虑他们的相加问题,两个整数的和以二进制形式存放在局偶n+1个元素的数组C中,请给出这个问题的形式化描 述,并写出伪代码
解: 形式化描述: A[1…..n],其中A[i] =0 或者 = 1, B[1…..n],其中B[i]=0 或者B[i]=1, C[i….n+1],其中C[i]=0 或者C[i]=1,
add描述: C[i] = (A[i]+B[i]+flag)%2, 其中:如果C[i-1] <2, flag=0,反之 flag=1, (当i=1时 ,flag=0)
伪代码如下:
BINARY_ADD(A[1….n],B[1…n],C[1…..n+1])
flag <- 0
for i <- 1 upto n
value <- A[i] + B[i] + flag
C[i] <- value mod 2
if value < 2 then flag <- 0
else then flag <- 1
endfor
C[n+1] <- flag
2-2:算法分析
2-2-1: 解: Θ(n3)
2-2-2: 解: 伪代码:
SELECTION_SORT(A[1…..n])
for i <- 1 upto n-1
key <- A[i]
Index <- i
for j <- i+1 upto n
if A[j] < key
key <- A[j]
Index <- j
A[Index] <- A[i]
A[i] <- key
end
循环不变式:在迭代k次后,A[1…k]已经按非降序排列好,且A[k+1….n]比A[1…k]中的元素都大
因为在执行n-1次循环之后,最后只剩下最后一个元素,所以它是剩下的所以元素中最小的
最佳和最坏情况都是 Θ(n2)
2-2-3 解答:假定每个元素被搜索的概率为p,则有pn<1(因为有可能搜索的元素不在数组中),那么平均查找的元素数PFind为1/p *(1+….+n) + (1-pn)*n, 最坏的情况为n,我们很容易获知PFind最小的时候为n/2,最大的时候为n,所以平均和最坏情况运行时间都为Θ(n)
2-2-4 解答: 我们可以首先设计一个针对该问题的一个特定输入,使得这个特定输入是最好的情况,然后不断修改算法,使其针对该特列的运行时间达到最佳
2-3 算法设计
2-3-1: 解: 3 9 26 38 41 49 52 57
3 26 41 52 9 38 49 57
3 41 26 52 38 57 9 49
3 41 52 26 38 57 9 49
2-3-2: 解:MERGE(A[1…..n], A, p, q, r)
n1 <- q-p+1
n2 <- r-q
create array L[1….n1] and R[1…..n2]
for i <- 1 upto n1
L[i] <- A[p+i-1]
for i <- 1 upto n2
R[i] <- A[q+i]
i <- 1, j <- 1
for k <- p upto r
if L[i] < R[j]
then A[k] <- L[i]
i <- i+1
if i = n1+1
for k <- k+1 upto r
A[k] <- R[j]
j <- j+1
break
else A[k] <- R[i]
j <- j+1
if j = n2+1
for k <- k+1 upto r
A[k] <- L[i]
i <- i+1
break
2-3-3: 解: 当n=2时,有T(2) = nlgn = 2*lg2 = 2 成立
假设n=2k时成立,即有T(2k) = 2k * lg(2k) = k*2k
当n = 2k+1时,有:T(2k+1) = 2*T(2k+1/2) + 2k+1= 2*k*2k +2k+1 = (k+1)*2k+1
综上得证
2-3-4: 解: T(n) = Θ(1) n=1 or 2
= T(n-1)+ Θ(n) n>2
2-3-5: 解:伪代码如下:
BINARY-SEARCH(A[1….n],v)
b <- length(A)
a <-1
while a>b
n <- (a+b)/2
if A[n] = v
return n
else if A[n] > v
b = n
else
a = n
很显然算法在最坏情况下运行时间为Θ(lgn)
2-3-6: 解:不能,因为虽然对于某次迭代,查找的次数变为了lgj,但是移动复制的次数认为j
2-3-7 解:算法思路:首先对S[1…n]进行非降序排序,然后设置两个指向标i=1,j=n,执行下面的操作:
S[i] + S[j] = X, 返回true
< X, i = i+1
> X, j = j-1
如果 i>j 返回false
正确性说明:很显然,如果S[i] + S[j]< X,则如果存在一个a,使得S[a] + S[j] = x,那么这个S[a]一定要大于s[j],换句话说a要大于i,另外 一种情况是存在一个a,使得S[i] + S[a] = x,那么这个S[a]一定要大于s[j],换句话说a要大于j,下面我们说明第二种情况不存在,在执行到j 前,因为偏移量总是1,所以必然会执行到a,这个时候在i之前的所有数,与S[a]相加是要小于X的,根据上面的公式,会移动前半段,即变动i,直 到到达i,所以不存在,同理可以说明>X的情况,因此如果算法返回false的话,算法可以验证那么对于任意的i,都不存在j使得其和等于X
时间复杂度分析:排序为Θ(nlgn),之后为Θ(n),算法的时间复杂度为Θ(nlgn)
思考题
解:a>、对单个子列表排序时间为ck2,整个列表排序时间为n/k*ck2为cnk,所以时间复杂度为Θ(nk)
b>、很显然,该分治算法一共要分的层数为lg(n/k)次,而每一层,合并算法的时间复杂度为Θ(n),所以总个的时间复杂度为Θ(nlg(n/k))
c>、 很显然Θ(nlg(n/k)) < Θ(nlgn),所以关键要考察nk,即k<lgn
d>、当输入好的情况比较多的时候,那么k应该选择比较大的值,当输入不好的时候,选择比较小的值
解:a> 不会
b> 循环不变式为: A[j] < A[k],其中 j <= k <= n
初始化: 当迭代开始前是成立的,因为只有一个元素
保持:当第t轮迭代保持这个性质,到t+1轮时,可以知道A[t]是后面所有书中最小的,那么只需要比较A[t-1]和A[t]的大小就可以知道后面t+1个数中最小了,即保持了循环不变式
终止:将j=i带入,可以知道A[i]满足该性质
c、循环不变式为:对于i,前面i个数按顺序排列,且都不大于后面的数,(证明略)
d、最坏情况为Θ(n*n),可以参考逆序对,因为交换一次要消除一个逆序对
解:a> 因为该公式,乘法和加法的次数在一个数量级上面,又因为乘法的运行时间要远高于加法,所以以乘法作为衡量标准,那么可知,它的渐进时间为Θ(k) b> k + k-1 + k-2 + …… + 1 = k(k+1)/2,可知渐进时间为Θ(k*k)
c>、初始化: 有y=0,i = n,可计算右边式子,根据条件可知为y=0,所以初试化满足循环不变式
保持:当第i=j满足时,考察i=j-1,很容易利用证明也满足(不好写公式,呵呵)
终止:当循环结束的时候,讲i替换成0,右边式子就是原公式,满足
d>、对于任意j, 0<= j <=n,有系数aj,观察公式可知,他被j个括号所包含,而没展开一个括号,那么就要乘以一个x,故对于右边式子,展开后,对于系数aj,一共要乘以j个x,正好是左边对应系数aj的项,即得证
解: a>、 (8 6) (2 1) ( 3 1) ( 8 1 ) (6 1)
b>、降序排列的时候, 含有n*(n-1)/2 个逆序对
c>、相等,因为每移动一个数,就会消除一个逆序对,知道所有的逆序对都消除完,排序也就完成了
d>、 这里就不给出具体算法了,只需要在归并排序合并步骤时,每移动一个右边的数,查看他左边数组还剩多少数,然后将其总数相加,就可以得到逆序对的数目了
如有错误,或是更好的解答方法,望路过的牛人指出,谢谢