算法导论8.2 Exercises 答案

8.2-1

第一次循环结束后,得到数组$C=\langle2,2,2,2,1,0,2\rangle$.

第二次循环结束后,得到数组$C=\langle2,4,6,8,9,9,11\rangle$.

那么第三次循环,依次得到:

$\begin{array}{ll}
B&C\
\langle\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2}\rangle & \langle2,4,6,8,9,9,11\rangle\
\langle\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},2,\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2}\rangle & \langle2,4,5,8,9,9,11\rangle\
\langle\phantom{2},\phantom{2},\phantom{2},\phantom{2},\phantom{2},2,\phantom{2},3,\phantom{2},\phantom{2},\phantom{2}\rangle & \langle2,4,5,7,9,9,11\rangle\
\langle\phantom{2},\phantom{2},\phantom{2},1,\phantom{2},2,\phantom{2},3,\phantom{2},\phantom{2},\phantom{2}\rangle & \langle2,3,5,7,9,9,11\rangle\
\langle\phantom{2},\phantom{2},\phantom{2},1,\phantom{2},2,\phantom{2},3,\phantom{2},\phantom{2},6\rangle & \langle2,3,5,7,9,9,10\rangle\
\langle\phantom{2},\phantom{2},\phantom{2},1,\phantom{2},2,\phantom{2},3,4,\phantom{2},6\rangle & \langle2,3,5,7,8,9,10\rangle\
\langle\phantom{2},\phantom{2},\phantom{2},1,\phantom{2},2,3,3,4,\phantom{2},6\rangle & \langle2,3,5,6,8,9,10\rangle\
\langle\phantom{2},\phantom{2},1,1,\phantom{2},2,3,3,4,\phantom{2},6\rangle & \langle2,2,5,6,8,9,10\rangle\
\langle\phantom{2},0,1,1,\phantom{2},2,3,3,4,\phantom{2},6\rangle & \langle1,2,5,6,8,9,10\rangle\
\langle\phantom{2},0,1,1,2,2,3,3,4,\phantom{2},6\rangle & \langle1,2,4,6,8,9,10\rangle\
\langle0,0,1,1,2,2,3,3,4,\phantom{2},6\rangle & \langle0,2,4,6,8,9,10\rangle\
\langle0,0,1,1,2,2,3,3,4,6,6\rangle & \langle0,2,4,6,8,9,9\rangle\
\end{array}$

8.2-2

考虑两个下标$i,j$满足$A[i]=A[j]=x,i< j$。第11行的逆序for循环将会使得$A[j]$先被访问,$A[i]$后被访问。当访问到$A[j]$时,数$A[j]$被放置到了数组$B$中的位置$C[x]$,接下来$C[x]$将会自减$1$。由于这个for循环中,$C$数组中的所有值只会递减,不会递增,因此当访问到$A[i]$时,此时的$C’[x]<C[x]$,即彼时访问$A[j]$前的$C[x]$。因此,$A[i]$将会放置到位置$C’[x]$中,保持了相对顺序。因此,这种实现下的计数排序是稳定的。

8.2-3

将循环正序,并不会影响排序的正确性。只要键相同,先后放置顺序并不重要,不同元素间的顺序将仍然保持。排序程序转换成不稳定的原因是对于两个下标$i,j$满足$A[i]=A[j]=x,i< j$,由于$i$先被遍历,$j$后被遍历,但是第11行的for循环下,$C$数组仍然是递减的,这将导致排序后,$A[i]$会被放置在$A[j]$的后面,因此仍然是不稳定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
COUNTING-SORT'(A, n, k)
let B[1 : n] and C[0 : k] be new arrays
for i = 0 to k
C[i] = 0
for j = 1 to n
C[A[j]] = C[A[j]] + 1
// C[i] now contains the number of elements equal to i.
s = 0
for i = 0 to k
c = C[i]
C[i] = s
s += c
// Copy A to B, starting from the end of A.
for j = 1 to n
C[A[j]] = C[A[j]] + 1 // to handle duplicate values
B[C[A[j]]] = A[j]
return B

8.2-4

初始化:一开始值为$n$的循环迭代前,$C[i]$表示数组$A$中有多少个数小于等于$i$,也就是最后一个$i$所放置的位置,循环不变量成立。

保持:在值为$i$的循环开始前,$C[A[i]]$代表着当前数$C[A[i]]$中应该放置的位置。第12行的代码将$A[i]$放置到正确的位置,第13行将$C[A[i]]$自减$1$,如果存在下一个$A[j]=A[i]=x,j< i$,那么它确实应该放置在$C[A[i]]-1$处,因为两个数相同,应该放在一起;如果不存在,那么$C[A[i]]$的值永远不会被访问到,可以忽略。因此,值为$i$的循环完成后,数组$C$的含义仍然保持,循环不变量成立。

终止,循环完成后,数组$C$中所有值都将被忽略,数组$B$是个有序的数组,循环正确终止。

8.2-5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 没有卫星数据,意味着不需要考虑排序的稳定性,直接赋值即可。
COUNTING-SORT''(A, n, k)
let C[0 : k] be new array
for i = 0 to k
C[i] = 0
for j = 1 to n
C[A[j]] = C[A[j]] + 1
// C[i] now contains the number of elements equal to i.
p = 0
for i = 0 to k
for j = 1 to C[i]
p += 1
A[p] = i
return A

8.2-6

可以发现,算法ANSWER-QUERY的第10行中,每次循环都是$O(1)$常数级别的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//数组Q表示询问列表,Q[i].a和Q[i].b表示题中的a和b。 
ANSWER-QUERY(A, n, k, Q, q)
let B[1 : q], C[0 : k] be new array
for i = 0 to k
C[i] = 0
for j = 1 to n
C[A[j]] = C[A[j]] + 1
// C[i] now contains the number of elements equal to i.
for i = 1 to k
C[i] = C[i] + C[i – 1]
// C[i] now contains the number of elements less than or equal to i.
for i = 1 to q
s = C[Q[i].b]
// 减去区间[0, a)中的所有数,为了避免越界,需要进行判断。
if Q[i].a > 0
s = s - C[Q[i].a - 1]
B[i] = s
return B

8.2-7

基本思想是:通过对每个浮点数乘以$10^d$转换成整数,再统计到下标中,然后排序完成后再转换成浮点数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
COUNTING-SORT-FLOAT(A, n, k, d)
// 假设POW(10, d)是一个常数,能以O(1)的时间被计算出。
K = k * POW(10, d)
let B[1 : n], C[0 : K] be new array
for i = 0 to K
C[i] = 0
for j = 1 to n
t = A[j] * POW(10, d)
C[t] = C[t] + 1
// C[i] now contains the number of elements equal to i.
for i = 1 to K
C[i] = C[i] + C[i – 1]
// C[i] now contains the number of elements less than or equal to i.
for j = n downto 1
t = A[j] * POW(10, d)
B[C[t]] = A[j]
C[t] = C[t] – 1
return B