算法导论课后习题解析 第二章
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比特序列
Output: 一个n+1比特序列
利用全加器的逻辑位运算可以简单得到每一位的结果和进位。
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
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
如果每个元素被找的几率相同且必定查找成功的时候,平均比较次数
当元素不存在或者是最后一个元素的时候需要比较的次数最多,为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时,
假设当n=k时有
那么当n=2k时有
得证。
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
|
所以该算法花费时间的递推公式为
2.3-6
不可以,因为虽然每次查找的时间复杂度为
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
所以不存在这样的两个数。
复杂度分析:第一步归并排序的时间复杂度为
2-1
a. 每个子序列的元素数量为k,最差的情况下需要进行
c. 由前两问的结果可知,复杂度为
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. 循环中的运算可以在常量时间内完成,所以复杂度是
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. 拥有逆序数最多的是
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序列中的元素