《算法导论》[第2章] 算法入门-[2.1] 插入排序
|概念回顾|
循环不变式的三个性质:
- 初始化:它在循环的第一轮迭代开始之前,应该是正确的。
- 保持:如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。
- 终止:当循环结束时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。
伪代码中的约定:
- 书写上的“缩进”表示程序中的分程序(程序块)结构。
- while,for,repeat等循环结构和if,then,else条件结构与Pascal中相同。
- 符号 "▷” 表示后面部分是个注释。
- 多重赋值 i←j←e 是将表达式e的值赋给变量i和j;等价于 j←e,再进行赋值 i←j。
- 变量(如i,j和key等)是局部给定过程的。
- 数组元素是通过“数组名[下标]”这样的形式来访问的。
- 复合数据一般组织成对象,它们是由属性(attribute)和域(field)所组成的。
- 参数采用按值传递方式:被调用的过程会收到参数的一份副本。
- 布尔运算符"and”和"or”都是具有短路能力。
|习题解答|
2.1-1 以图2-2为模型,说明INSERTION-SORT在数组A=<31,41,59,26,41,58>上的执行过程。
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 重写过程INSERTION-SORT,使之按非升序(而不是按非降序)排序。
伪代码如下:
INSERTION-SORT(A)
for j←to length[A]
do key←A[J]
i←j-1
while i>0 and A[i]<key
do A[i+1]←A[i]
i←i-1
A[i+1]←key
2.1-3 考虑下面的查找问题:
输入:一列数A=<a1,a2,…,an>和一个值V。
输出:下标i,使得V=A[i],或者当V不再A中出现时为NIL。
写出针对这个问题的线性查找的伪代码,它顺序地扫描整个序列以查找V。利用循环不变式证明算法的正确性。确保所给出的循环不变式满足三个必要的性质。
伪代码如下:
LINEAR-SEARCH(A,V)
flag←0
for i←1 to length[A]
do if A[i]==V
then print i
flag←1
if flag==0
then print NIL
循环不变式的证明如下:
初始化:首先,先来证明在第一轮迭代之前,它是成立的。此时,flag初始化为0,表示未找到这样一个小标i,使得A[i]=V.若为1,则表示已找到一个或多个这样的下标。那么,很显然,在还未开始之前flag=0是正确的,也就证明了循环不变式在循环的第一轮迭代开始之前是成立的。
保持:接下来证明每一轮循环都能使循环不变式保持成立。我们看到,在第一个for循环体内,随着i逐个从1到遍历完整个数列的过程中,只要有一个下标i,使得A[i]等于V,那么在输出i的同时,将flag重新标记为1,表示已找到。无论接下来是否还有这样满足条件的i出现,flag的值不变,仍为1,反之,若遍历完之后,没有找到这样的一个i,那么flag在这个for循环中未做任何改变,仍为0。所以,循环不变式的第二个性质也成立。
终止:对此线性查找算法来说,当i大于length[A]的值(即遍历完整个A数列后),for循环结束。这时,如果flag未改变(即flag=0),则说明未能找到这样的下标i,输出NIL;反之,若已在for循环中被修改为1,则不会运行此步语句,这也就意味着该算法是正确的。
2.1-4 有两个各存放在数组A和B中的n位二进制整数,考虑它们的相加问题。两个整数的和以二进制形式存放在具有(n+1)个元素的数组C中。请给出这个问题的形式化描述,并写出伪代码。
形式化描述如下:
首先,初始化数组C,使其n+1各元素的值都为0。接着,利用for循环用i同时从n到1反向遍历A、B数组,每遍历一步,作一个判断:若A[i]+B[i]>1,则C[i]=C[i]+1;反之,C[i+1]=A[i]+B[i]。最后,当i=0时,for循环结束,此时C数组中存储的即是所求的结果。
伪代码如下:
BINRARY-SUM(A,B,C)
for i←1 to n+1
do C[i]←0
for i←n to 1
do if A[i]+B[i]>1
then C[i]=C[i]+1
else C[i+1]=A[i]+B[i]