本章主要讲述在代码测试和调试过程中使用 “脚手架方法”。
“脚手架” 就是在代码中添加的冗余测试代码(通常输出程序的局部变量)。
1首先请看一个二分搜索的代码,要看仔细了:
1 function bsearch(t)
2 {
3 l=1;u=n;//l是头,u是尾
4 while(l<=u)
5 {
6 m=int((l+u)/2);
7 print(l,m,u);
8 if(x[m]<t) l=m;
9 else if(x[m]>t) u=m;
10 else return m;
11 }
12 return 0;
13 }
14
咋一看好像没有问题,算法的基本结构都对的,但是代码确实有问题。
那么我们需要建立实例进行测试,对于数组x[n]的长度,不太大亦不能太小,作者认为n取5最好。对数组赋初值。
for(i=1;i<=n;i++) x[i]=10*i;
当bsearch(50)时,出现如下提示:
1 3 5
3 4 5
4 4 5
4 4 5
4 4 5
......
程序出现死循环,因为:
4<=5且int((4+5)/2)=4
当第一次看到着,我有种像是被人抓到把柄似的,我找到了自己以前写的二分查找的代码
我回忆了下,当初是因为考虑到x[m]已经考虑过,不需要再考虑。所以我写的是
if(x[m]<t) l=m+1; else if(x[m]>t) u=m-1;
这个想法使我避免了这个错误,事实上我不知道这是为了避免错误而必须采取的。
那么你是否曾经意识到这一点呢?
2下面看第二个例子,一个选择算法
1 function select(l,u,k)
2 {
3 if(l<u){
4 swap(l,l+int((u-l+1)*rand()));
5 m=1;
6 for(i=l+1;i<=u;i++)
7 if(x[i]<x[l])
8 swap(++m,i);
9 swap(l,m);
10 if(m<k) select(m+1,u,k);
11 else if(m>k) select(l,m-1,k);
12 }
13 }
这个算法是Hoare写的用于求数组x[n]中第k小的元素,数组是无序的,运行后,x[k]就是要找的数据。
光看看可能看不明白,可以找个小纸条自己比划比划。写的凝练了,看起来简洁,但是更不容易直接理解。
熟悉Hoare的朋友知道,快速排序就是他创造的。
快速排序也叫快速定位,每次确定一个数据的位置。上面算法还混合了概率算法(sherwood)思想来平滑平均性能。
作者为了去掉尾递归部分(可能是出于性能考虑) 得到改进后的代码如下:
1 function select(l,u,k)
2 {
3 l=1,u=n;
4 while(l<u){
5 print(l,u);
6 swap(l,l+int((u-l+1)*rand()));
7 m=1;
8 comps=comps+u-1;//记录比较次数
9 for(i=l+1;i<=u;i++)
10 if(x[i]<x[l])
11 swap(++m,i);
12 swap(l,m);
13 if(m<k) l=m+1;
14 else if(m>k) u=m-1;
15 }
16 }
作者给出了一个输入使得程序出现错误。就是当m==k的时候可能会产生死循环。
作者给出的改进方法是将if(m<k)改为if(m<=k),我不敢苟同。
第一:
我发现作者改进后还是有错误:
我的测试代码是:

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <time.h>
4 int x[6];
5 void init()
6 {
7 int i;
8 for(i=1;i<6;i++)
9 {
10 x[i]=(int)rand();
11 printf("%d ",x[i]);
12 }
13 printf("\n");
14 }
15 void swap(int a,int b)
16 {
17 int temp=x[a];
18 x[a]=x[b];
19 x[b]=temp;
20 }
21 void select(int l,int u,int k)
22 {
23 int i,m,comps=0;//记录比较次数
24 while(l<u){
25 printf("%d %d\n",l,u);
26 swap(l,l+(int)(rand()%(u-l+1)));
27 m=1;
28 comps=comps+u-1;
29 for(i=l+1;i<=u;i++)
30 if(x[i]<x[l])
31 swap(++m,i);
32 swap(l,m);
33 if(m<=k) l=m+1;
34 else if(m>k) u=m-1;
35 }
36 }
37 void main()
38 {
39 int i;
40 srand(time(0));
41 init();
42 select(1,5,3);
43 for(i=1;i<6;i++)
44 {
45 printf("%d ",x[i]);
46 }
47 printf("\n");
48
49 }
发现的错误如下:
输出显示第三个数不是8504,具体原因我不分析了,如果你有兴趣可以琢磨琢磨。
第二:
我们的目标是为了使第i个数存放在第i个位置。
如果运气好,随机选到的数刚好是要找的数,那么仅第一次循环它就被放到正确位置,此时程序就可以结束了。
就算第一次没被选到,后面也会被选到。也就是说当发现m=k的时候,程序就应该终止排序。
添加代码
if(m<k) l=m+1;
else if(m>k) u=m-1;
else break;
我进行了初步测试,没发现问题。而且其print输出也明显减少。