您的位置:首页 > 职场人生

程序员编程艺术第二十五章:Jon Bentley:90%无法正确实现二分查找

2012-02-25 11:41 204 查看

第二十五章:二分查找实现(Jon Bentley:90%程序员无法正确实现)

作者:July

出处:结构之法算法之道

引言

Jon Bentley:90%以上的程序员无法正确无误的写出二分查找代码。也许很多人都早已听说过这句话,但我还是想引用《编程珠玑》上的如下几段文字:

“二分查找可以解决(预排序数组的查找)问题:只要数组中包含T(即要查找的值),那么通过不断缩小包含T的范围,最终就可以找到它。一开始,范围覆盖整个数组。将数组的中间项与T进行比较,可以排除一半元素,范围缩小一半。就这样反复比较,反复缩小范围,最终就会在数组中找到T,或者确定原以为T所在的范围实际为空。对于包含N个元素的表,整个查找过程大约要经过log(2)N次比较。

多数程序员都觉得只要理解了上面的描述,写出代码就不难了;但事实并非如此。如果你不认同这一点,最好的办法就是放下书本,自己动手写一写。试试吧。

我在贝尔实验室和IBM的时候都出过这道考题。那些专业的程序员有几个小时的时间,可以用他们选择的语言把上面的描述写出来;写出高级伪代码也可以。考试结束后,差不多所有程序员都认为自己写出了正确的程序。于是,我们花了半个钟头来看他们编写的代码经过测试用例验证的结果。几次课,一百多人的结果相差无几:90%的程序员写的程序中有bug(我并不认为没有bug的代码就正确)。

我很惊讶:在足够的时间内,只有大约10%的专业程序员可以把这个小程序写对。但写不对这个小程序的还不止这些人:高德纳在《计算机程序设计的艺术 第3卷 排序和查找》第6.2.1节的“历史与参考文献”部分指出,虽然早在1946年就有人将二分查找的方法公诸于世,但直到1962年才有人写出没有bug的二分查找程序。
”——乔恩·本特利,《编程珠玑(第1版)》第35-36页。

你能正确无误的写出二分查找代码么?不妨一试。

二分查找代码

二分查找的原理想必不用多解释了,不过有一点必须提醒读者的是,二分查找是针对的排好序的数组。OK,纸上读来终觉浅,觉知此事要躬行。我先来写一份,下面是我写的一份二分查找的实现(之前去某一家公司面试也曾被叫当场实现二分查找,不过结果可能跟你一样,当时就未能完整无误写出),有任何问题或错误,恳请不吝指正:

01.//二分查找V0.1实现版  
02.//copyright@2011 July  
03.//随时欢迎读者找bug,email:zhoulei0907@yahoo.cn。  
04.  
05.//首先要把握下面几个要点:  
06.//right=n-1 => while(left <= right) => right=middle-1;  
07.//right=n   => while(left <  right) => right=middle;  
08.//middle的计算不能写在while循环外,否则无法得到更新。  
09.  
10.int binary_search(int array[],int n,int value)  
11.{  
12.    int left=0;  
13.    int right=n-1;  
14.    //如果这里是int right = n 的话,那么下面有两处地方需要修改,以保证一一对应:  
15.    //1、下面循环的条件则是while(left < right)  
16.    //2、循环内当array[middle]>value 的时候,right = mid  
17.  
18.    while (left<=right)          //循环条件,适时而变  
19.    {  
20.        int middle=left + ((right-left)>>1);  //防止溢出,移位也更高效。同时,每次循环都需要更新。  
21.  
22.        if (array[middle]>value)  
23.        {  
24.            right =middle-1;   //right赋值,适时而变  
25.        }   
26.        else if(array[middle]<value)  
27.        {  
28.            left=middle+1;  
29.        }  
30.        else  
31.            return middle;    
32.        //可能会有读者认为刚开始时就要判断相等,但毕竟数组中不相等的情况更多  
33.        //如果每次循环都判断一下是否相等,将耗费时间  
34.    }  
35.    return -1;  
36.}

注意点:

注释里已经说得很明白了,可还是会有不少朋友犯此类的错误:

//首先要把握下面几个要点:


//right=n-1 => while(left <= right) => right=middle-1;


//right=n => while(left < right) => right=middle;


//middle的计算不能写在while循环外,否则无法得到更新。


还有一个最最常犯的错误是@土豆:

middle= (left+right)>>1; 这样的话left与right的值比较大的时候,其和可能溢出。
(转自/article/1361440.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐