算法导论课后习题解析 第二章
2.1-1
初始 31 41 59 26 41 58
第一遍 31 41 59 26 41 58
第二遍 31 41 59 26 41 58
第三遍 26 31 41 59 41 58
第四遍 26 31 41 41 59 58
第五遍 26 31 41 41 58 59
2.1-2
把顺序改成非递增只要把判断大小时的条件改成小于即可
1
2
3
4
5
6
7
8
|
Insertion-Sort(A)
for
j = 2 to A.length
key = A[j]
i = j - 1
while
i > 0 and A[i] < key
A[i + 1] = A[i]
i = i - 1
A[i + 1] = key
|
2.1-3
1
2
3
4
5
|
Linear-Search(A, v)
for
i = 1 to A.length
if
A[i] == v
return
i
return
NIL
|
- Initialization: 一开始i=1,说明子序列[1, 0]即空集中不包含所找的v。
- Maintenance: 接下来,每次循环开始的时候都有子序列[1, i - 1]中不包含所找的v,而每次循环都判断A[i]是否是v,当不是v的时候说明子序列[1, i]中都不包含要找的v,这为下一次循环提供了前提条件。
- Termination: 最后当循环退出的时候有两种情况:当发现A[i]等于v的时候返回要找元素的下标i;当循环进行到i=A.length次后,这时说明子序列[1, A.lenght]即整个序列中都没有要找的v,这个时候返回NIL。
2.1-4
Input: 两个n比特序列
A={a1,a2,…,an}
A={a1,a2,…,an}
B={b1,b2,…,bn}
B={b1,b2,…,bn}
Output: 一个n+1比特序列
C={c1,c2,…,cn+1}
C={c1,c2,…,cn+1} 使得等式
(cn+1cn…c2c1)2=(anan−1…a2a1)2+(bnbn−1…b2b1)2
(cn+1cn…c2c1)2=(anan−1…a2a1)2+(bnbn−1…b2b1)2 成立
利用全加器的逻辑位运算可以简单得到每一位的结果和进位。
1
2
3
4
5
6
7
8
9
10
11
|
Binary-Addition(A, B)
inc = 0
i = 1
while
i <= A.length
k = A[i] xor B[i]
g = A[i] and B[i]
C[i] = k xor inc
inc = inc and k or g
i = i + 1
C[i] = inc
return
C
|
2.2-1
Θ(n3)
Θ(n3)
2.2-2
1
2
3
4
5
6
7
8
9
|
Selection-Sort(A)
for
i = 1 to A.length - 1
min = i
for
j = i + 1 to A.length
if
A[j] < A[min]
min = j
tmp = A[min]
A[min] = A[i]
A[i] = tmp
|
- Initialization: 一开始i=1,说明子序列[1, 0]即空集有序。
- Maintenance: 接下来,每次循环开始的时候都有子序列[1, i - 1]都是有序的,且[1, i - 1]中的元素都小于[i, A.length]中的元素。而每次循环都把[i, A.length]中最小的元素与A[i]交换,由于A[i]大于[0, i - 1]中的任意元素,所以[0, i]中的元素有序,且[0, i]中的元素都小于[i + 1, A.length]中的元素,这为下一次循环提供了前提条件。
- Termination: 最后当循环退出的时候[1, A.length - 1]中的元素有序,且都小于A[A.length],所以整个[1, A.length]序列有序。
2.2-3
第i个元素为所找元素需要的比较次数为i
如果每个元素被找的几率相同且必定查找成功的时候,平均比较次数
c=(1+2+...+n)/n=(n+1)/2
c=(1+2+...+n)/n=(n+1)/2
当元素不存在或者是最后一个元素的时候需要比较的次数最多,为n次。
平均和最坏时间复杂度都是
Θ(n)
Θ(n)
2.2-4
当输入有可能就是结果(比如排序)或者能够直接算出的情况(比如全零矩阵相乘),直接输出结果。
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Merge(A, p, q, r)
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[i] = A[q + j]
i = 1, j = 1, k = p
while
i <= n1 and j <= n2
if
L[i] <= R[j]
A[k] = L[i]
i = i + 1
else
A[k] = R[j]
j = j + 1
k = k + 1
while
i <= n1
A[k] = L[i]
i = i + 1
k = k + 1
while
j <= n2
A[k] = R[j]
j = j + 1
k = k + 1
|
2.3-3
当n=2时,
T(n)=2∗lg2=2
T(n)=2∗lg2=2 满足;
假设当n=k时有
T(k)=k∗lgk
T(k)=k∗lgk ,
那么当n=2k时有
T(2k)=2T(k)+2k=2klgk+2k=2klgk+2klg2=2klg2k
T(2k)=2T(k)+2k=2klgk+2k=2klgk+2klg2=2klg2k
得证。
2.3-4
1
2
3
4
5
6
7
8
9
|
Insertion-Sort(A, n)
if
n > 1
Insertion-Sort(A, n - 1)
i = n - 1
key = A[n]
while
i > 0 and A[i] > key
A[i + 1] = A[i]
i = i - 1
A[i + 1] = key
|
2.3-5
1
2
3
4
5
6
7
8
9
10
11
|
Binary-Search(A, v)
low = 1, high = A.length
while
low <= high
mid = (low + high) / 2
if
A[mid] == v
return
mid
else
if
A[mid] < v
low = mid + 1
else
high = mid - 1
return
NIL
|
所以该算法花费时间的递推公式为 T(n)=T(⌊n/2⌋)+Θ(1) T(n)=T(⌊n/2⌋)+Θ(1),得出时间复杂度为 T(n)=Θ(lgn) T(n)=Θ(lgn)
2.3-6
不可以,因为虽然每次查找的时间复杂度为
Θ(lgn)
Θ(lgn) 但是把元素放到正确的位置仍然需要移动同样多的元素,所以复杂度没有变化,在n比较大的时候系数可能会变小一点。
2.3-7
1
2
3
4
5
6
7
8
9
10
11
12
|
Sum-Exist(A, x)
Merge-Sort(A)
low = 1, high = A.length
while
low < high
sum = A[low] + A[high]
if
sum == x
return
true
else
if
sum > x
high = high - 1
else
low = low + 1
return
false
|
- Initialization: 一开始将序列非递减排序,有
a1≤a2≤⋯≤an
a1≤a2≤⋯≤an,此时low=1,high=n,必然有:
∀i∈[1,low−1],j∈[low,high]都有ai+aj<x(1) (1)∀i∈[1,low−1],j∈[low,high]都有ai+aj<x∀i∈[high+1,n],j∈[low,high]都有ai+aj>x(2) (2)∀i∈[high+1,n],j∈[low,high]都有ai+aj>x∀i∈[1,low−1],j∈[high+1,n]都有ai+aj≠x(3) (3)∀i∈[1,low−1],j∈[high+1,n]都有ai+aj≠x
- Maintenance: 接下来,每次循环都判断
sum=alow+ahigh
sum=alow+ahigh的大小。
当 sum<x sum<x时,说明 ∀i∈[low+1,high]都有alow+ai<x ∀i∈[low+1,high]都有alow+ai<x,这时将low增1使得式(1)得到满足,从而使式(3)得到满足,式(2)不变;
当 sum>x sum>x时,说明 ∀i∈[low,high−1]都有ahigh+ai>x ∀i∈[low,high−1]都有ahigh+ai>x,这时将high减1使得式(2)得到满足,从而使式(3)得到满足,式(1)不变;这为下一次循环提供了前提条件; - Termination: 最后当循环退出的时候有两种情况。
当判断 sum=x sum=x时,说明找到了元素 ai,aj使得ai+aj=x ai,aj使得ai+aj=x;
当判断 low=high low=high时令 k=low=high k=low=high,由式(1)可得 ∀i∈[1,k−1]都有ai+ak<x ∀i∈[1,k−1]都有ai+ak<x,由式(2)可得 ∀i∈[k+1,n]都有ai+ak>x ∀i∈[k+1,n]都有ai+ak>x,由式(3)可得 ∀i∈[1,k−1],j∈[k+1,n]都有ai+aj≠x ∀i∈[1,k−1],j∈[k+1,n]都有ai+aj≠x
所以不存在这样的两个数。
复杂度分析:第一步归并排序的时间复杂度为 O(nlgn) O(nlgn),之后最坏的情况是没找到这样的两个数,需要对每个元素比较一次,时间复杂度为 O(n) O(n),所以总的时间复杂度为 O(nlgn) O(nlgn)。
2-1
a. 每个子序列的元素数量为k,最差的情况下需要进行
k(k−1)/2
k(k−1)/2次基础操作,一共有
n/k
n/k个子序列,所以总的基础操作次数为
c. 由前两问的结果可知,复杂度为 Θ(nk+nlgn/k) Θ(nk+nlgn/k)。要找到最大的k就应该使得 Θ(nk) Θ(nk)和 Θ(nlgn/k) Θ(nlgn/k)的增长率相等,这时 k=lgn k=lgn,即
2-2
a. 需要证明最终的序列是非递减的,还需要证明最终序列是原序列的一种排列。
b.
- Initialization: 一开始j=A.length,有A[j]不大于子序列[j, A.length]中的元素;
- Maintenance: 接下来,每次循环开始的时候都有A[j]不大于子序列[j, A.length]中的元素。而每次循环都把A[j]和A[j-1]中较小的元素放到A[j-1],则有A[j-1]不大于子序列[j-1, A.length]中的元素,然后让j减1,满足了循环初始的条件,由于只做了比较和交换操作,所以序列[i, A.length]只是原来序列的一种排列。
- Termination: 最后当循环退出的时候有A[i]不大于子序列[i, A.length]中的元素,即A[i]是子序列[i, A.length]中最小的之一。
- Initialization: 一开始i=1,[1, i-1]中的元素非递减,且[i, A.length]中的元素都不小于[1, i-1]中的元素;
- Maintenance: 接下来,每次循环开始的时候都有[1, i-1]中的元素非递减,且[i, A.length]中的元素不小于[1, i-1]中的元素。而每次循环都把A[i]和[i, A.length]中最小的之一交换,又A[i]不小于[1, i-1]中的元素,所以[1, i]中的元素非递减,且[i+1, A.length]中的元素都不小于[1, i]中的元素,这时i增加1,满足了循环初始的条件。
- Termination: 最后当循环退出的时候有[1, A.length-1]中的元素非递减,且A.length不小于[1, i-1]中的元素。所以整个序列[1, A.length]非递减。
2-3
a. 循环中的运算可以在常量时间内完成,所以复杂度是
Θ(1)
Θ(1),循环次数为n,所以总的时间复杂度为
Θ(n)
Θ(n)。
b.
1
2
3
4
5
6
7
8
|
Evaluate-Polynomial(A, x, n)
y = 0
for
k = 0 to n
t = 1
for
i = 1 to k
t = t * x;
y = y + A[k] * t
return
y
|
c. 当循环进行到最后一次时,i = 0,可得
d. 根据c的循环不变式可以保证该算法所得结果正确。
2-4
a. (8, 6) (2, 1) (3, 1) (8, 1) (6, 1)
b. 拥有逆序数最多的是
{n,n−1,⋯,3,2,1}
{n,n−1,⋯,3,2,1},其中任意两个数之间都是逆序的,所以总计
n(n−1)/2
n(n−1)/2个。
c. 逆序数应该与元素移动的数量相同。插入排序每次移动元素都将一个较大的数移到较小的数后面,使得总逆序数减少1,而排序完成后逆序数是0,所以开始的逆序数和元素移动的次数相同。
d.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
Merge-Inversion(A, p, q, r)
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[i] = A[q + j]
i = 1, j = 1, k = p, inv = 0
while
i <= n1 and j <= n2
if
L[i] <= R[j]
A[k] = L[i]
i = i + 1
else
inv = inv + n1 - i + 1
A[k] = R[j]
j = j + 1
k = k + 1
while
i v= n1
A[k] = L[i]
i = i + 1
k = k + 1
while
j <= n2
A[k] = R[j]
j = j + 1
k = k + 1
return
inv
Calculate-Inversion(A, p, r)
inv = 0
if
p < r
q = (p + r) / 2
inv = inv + Calculate-Inversion(A, p, q)
inv = inv + Calculate-Inversion(A, q, r)
inv = Merge-Inversion(A, p, q, r)
return
inv
|
在Merge过程中如果R序列中的元素 rj rj出现在L中的元素 li li之前,说明 rj rj与 [li,ln1] [li,ln1]中的所有元素都组成逆序数,一共有n1 - i + 1个。