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


    二分查找实现(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 页。
    你能正确无误的写出二分查找代码么?不妨一试。
    348
    二分查找代码
    二分查找的原理想必不用多解释了,不过有一点必须提醒读者的是,二分查找是针对的
    排好序的数组。OK,纸上读来终觉浅,觉知此事要躬行。我先来写一份,下面是我写的一
    份二分查找的实现(之前去某一家公司面试也曾被叫当场实现二分查找,不过结果可能跟你
    一样,当时就未能完整无误写出),有任何问题或错误,恳请不吝指正:
    //二分查找V0.1 实现版
    //copyright@2011 July
    //随时欢迎读者找bug,email:zhoulei0907@yahoo.cn。
    //首先要把握下面几个要点:
    //right=n-1 => while(left <= right) => right=middle-1;
    //right=n => while(left < right) => right=middle;
    //middle 的计算不能写在while 循环外,否则无法得到更新。
    int binary_search(int array[],int n,int value)
    {
    int left=0;
    int right=n-1;
    //如果这里是int right = n 的话,那么下面有两处地方需要修改,以保证一一对应:
    //1、下面循环的条件则是while(left < right)
    //2、循环内当array[middle]>value 的时候,right = mid
    while (left<=right) //循环条件,适时而变
    {
    int middle=left + ((right-left)>>1); //防止溢出,移位也更高效。同时,每次循环
    都需要更新。
    if (array[middle]>value)
    {
    right =middle-1; //right 赋值,适时而变
    }
    else if(array[middle]<value)
    {
    left=middle+1;
    }
    else
    return middle;
    //可能会有读者认为刚开始时就要判断相等,但毕竟数组中不相等的情况更多
    //如果每次循环都判断一下是否相等,将耗费时间
    }
    return -1;
    349
    }
    简单测试下,运行结果如下所示(当然,一次测试正确不代表程序便0 bug 了,且测试
    深度远远不够):
    测试
    也许你之前已经把二分查找实现过很多次了,但现在不妨再次测试一下。关闭所有网页,
    窗口,打开记事本,或者编辑器,或者直接在本文评论下,不参考上面我写的或其他任何人
    的程序,给自己十分钟到N 个小时不等的时间,立即编写一个二分查找程序。独立一次性
    正确写出来后,可以留下代码和邮箱地址,我给你传一份本blog 的博文集锦CHM 文件&&
    十三个经典算法研究带标签+目录的PDF 文档(你也可以去我的资源下载处下载:
    http://download.csdn.net/user/v_july_v)。
    350
    当然,能正确写出来不代表任何什么,不能正确写出来亦不代表什么,仅仅针对Jon
    Bentley 的言论做一个简单的测试而已。下一章,请见第二十六章:基于给定的文档生成倒
    排索引的编码与实践。谢谢。
    总结
    本文发表后,马上就有很多朋友自己尝试了。根据从朋友们在本文评论下留下的代码,
    发现出错率最高的在以下这么几个地方:
    1. 注释里已经说得很明白了,可还是会有不少朋友犯此类的错误:
    1. //首先要把握下面几个要点:
    2. //right=n-1 => while(left <= right) => right=middle-1;
    3. //right=n => while(left < right) => right=middle;
    4. //middle 的计算不能写在while 循环外,否则无法得到更新。
    2. 还有一个最最常犯的错误是@土豆:
    middle= (left+right)>>1; 这样的话left 与right 的值比较大的时候,其和可能溢出。
    各位继续努力。
    updated:各位,可以到此处0 积分下载本blog 最新博文集锦第6 期CHM 文件:
    http://download.csdn.net/detail/v_july_v/4020172。

  • 相关阅读:
    acdream 瑶瑶带你玩激光坦克 (模拟)
    acdream 小晴天老师系列——苹果大丰收(DP)
    acdream 小晴天老师系列——晴天的后花园 (暴力+剪枝)
    acdream 小晴天老师系列——竖式乘法(简单穷举)
    acdream LCM Challenge (最小公倍数)
    LeetCode Product of Array Except Self (除自身外序列之积)
    LeetCode Implement Trie (Prefix Tree) (实现trie树3个函数:插入,查找,前缀)
    字节流与字符流的区别
    oop第二章1知识点汇总
    抽象类和抽象方法的一些概念(转自百度)
  • 原文地址:https://www.cnblogs.com/zhizhan/p/4876221.html
Copyright © 2020-2023  润新知