《编程珠玑》之二分搜索

chapter4 + chapter5 书中的观点:

@1,程序员都是乐观主义者,他们总是试图走捷径;编写函数代码,并将其插入系统中,然后热切的期望它能运行。有时候这样行的通,但是有千分之九百九十九的概率,这样做会导致一场灾难!

@2,专业的程序员永远不会忘记,无论系统的行为咋看起来多么的神秘莫测,其背后总有合乎逻辑的解释。

@3,虽然第一篇关于二分搜索的论文在1946年就发表了,但是第一个没有错误的二分搜索程序却直到1962年才出现。

@4,关键词:“连续两轮仅首次运行正确”,函数调用的前置条件和后置条件,“契约编程” . 

 

        作者用两章的篇幅,介绍了编写 “正确” 程序的方法。包括从理论上(数学)层面证明二分搜索的正确性,用脚手架验证程序,用断言快速定位程序不合理的逻辑及变量值。

a,二分搜索的数学层面证明,对于已经升序排序的数组序列 a[b,e];找出 T 在a数组中的位置。m = (b+e)/2 ,如果 a[m] > T,那么 T 必然不在 a[m+1,e]  序列中; 如果 a[m] < T,那么 T 必然不在a[b,m-1] 序列中。因此每次比较,均可对查找的范围减半。

b,程序正确性验证的方法 ,以下面的程序为例:

        a,程序能否正常终止: 当输入  9 40  时,程序会陷入死循环! 为什么 b = mid +1,  e = mid -1 ;已经对每次的循环都保证缩小1,还是免不了死循环?问题在于,第二次循环 b=5, mid=9, e=8;这本来就是不合理的,mid值跑到 [b,e] 范围之外去了(这也可以作为断言的条件)。当 mid - 1 正好等于 8,因此下次计算mid时,还是  b=5, mid=9, e=8. 

       b,修正a的问题,将 mid = b + e/2  改成  mid = b + (e-b)/2.    继续测试。按常见的错误,将 b= mid +1 改成 b = mid ;  将 e = mid -1 改成 e = mid;继续验证。当 输入 0  -1;0  0;0  5;1  0;均能正常工作,输入 1  5 时,程序又进入死循环。不难分析 b = 0, mid = 0,e = 0;下一次循环还是  b = 0, mid = 0,e = 0;问题是,每次循环,查找的范围没有保证至少缩小1个位置范围。

 #include<stdio.h>
#include<assert.h>
int binarySearch(int arr[], int size, int value)
{
    assert(size >= 0);
    if(0==size)
    {
        return -1;
    }
    int b = 0;
    int e = size - 1;
    while(e >= b)
    {
        int mid = b + e/2;
        assert(mid >= 0 && mid < size);   //断言对于快速定位问题很有帮助
        printf("b:%d, mid:%d, e:%d\n",b,mid,e);  //有时在大型程序中,添加rintf打印调试,是个sample but powerful 的方法
        if(value > arr[mid])
            b = mid + 1;
        else if(value < arr[mid])
            e = mid - 1;
        else
            return mid;
    }
    return -1;
}


int main()
{
    //简单测试
    int s[10];
    for(int i=0;i<10;++i)
    {
        s[i]=5*i;
        printf("%d ",s[i]);
    }
    printf("\n");

    int n,v;
    //注意对边界情况做测试,一般能快速发现程序问题
    scanf("%d %d",&n,&v);
    while(n>=0 && n<=10)
    {
        printf("n:%d, v:%d \n",n,v);
        printf("index:%d\n",binarySearch(s,n,v));
        scanf("%d %d",&n,&v);
    }

    return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值