你真的能搞定二分查找吗 — 二分查找及变形

看到一篇很有意思的文章就转过来了。。。

原文:

首先引用一下《编程珠玑》中的两句话: 

  1. 尽管给了那么充裕的时间,只有大约10%的专业程序员能够写出正确的二分查找。
  2. 尽管第一个二分查找程序于1946年就公布了,但是第一个没有bug的二分查找程序在1962年才出现。

当时看到这的时候,我觉得有点夸张。这里不去讨论是否真的花了20年才人们才写出正确的代码,但这两句话至少告诉我们,不要小看二分搜索。

确实,写过bsearch的人都知道,迭代的出口很难在短时间内想清楚(反正我经常糊涂)。当然,常规的二分搜索大家现在基本都能写了,(写过这么多次,背也背下来了),但能写出来不代表真正理解了二分搜索。比如我今天在做一道题的时候就卡在了这里:从一个有序数组中找到大于x且最接近x的数。

说白了就是二分搜索,但是迭代的出口是什么?

先来看一下常规的二分查找的代码:

/**
 * 函数简介: 有序数组中搜索指定元素
 *
 * 参数: a  数组指针
 * 参数: n  元素数目
 * 参数: x  要搜索的值
 *
 * 返回值:  若找到,返回该元素下标
 *          若找不到,返回-1
 */
/* ---------------------------------------------------------------*/
int bsearch(int *a, int n, int x)
{
    int m;
    int l = 0, r = n -1;
 
    while( r >= l )
    {
        m = (l+r)/2;
        if(a[m] == x)
            return m;
        else if(a[m] < x)
            l = m+1;
        else
            r = m-1;
    }
    return -1;
}

考虑一下要查找的元素不在序列中的情况:函数会返回-1,那l和r分别指向什么呢?

可以证明:l指向大于x的第一个元素,r指向小于x的第一个元素。(为什么?迭代的最后一步必然是l和r指向同一元素,然后为什么会变得比r大呢?想想就明白了)

既然得出了这个结论,那么代码就好写了,但是还得注意一些边界条件,比如最后返回的时候检查l和r是否越界,如果越界就说明找不到。

代码如下,如果有错,欢迎讨论:

#include  <iostream>
using namespace std;
 
/* ---------------------------------------------------------------*/
/**
 * 函数简介: 有序数组中搜索指定元素
 *
 * 参数: a  数组指针
 * 参数: n  元素数目
 * 参数: x  要搜索的值
 *
 * 返回值:  若找到,返回该元素下标
 *          若找不到,返回-1
 */
/* ---------------------------------------------------------------*/
int bsearch(int *a, int n, int x)
{
    int m;
    int l = 0, r = n -1;
 
    while( r >= l )
    {
        m = (l+r)/2;
        if(a[m] == x)
            return m;
        else if(a[m] < x)
            l = m+1;
        else
            r = m-1;
    }
    return -1;
}
 
/* ---------------------------------------------------------------*/
/**
 * 函数简介:  在有序数组中查找比x大但是最接近x的数
 *
 * 参数: a  数组指针
 * 参数: n  元素数目
 * 参数: x  要搜索的值
 *
 * 返回值:  若找到,返回该元素下标
 *          若找不到,返回-1
 */
/* ---------------------------------------------------------------*/
int bsearch_more(int *a,int n, int x)
{
    int m;
    int l = 0, r = n -1;
 
    while( r >= l )
    {
        m = (l+r)/2;
        
        if(a[m] == x)
            return ( (m+1) >= n ? -1 : (m+1) );
        else if(a[m] < x)
            l = m+1;
        else
            r = m-1;
    }
    l = (l >= n ? -1 : l);
    return l;
}
 
/* ---------------------------------------------------------------*/
/**
 * 函数简介:  在有序数组中查找比x小但是最接近x的数
 *
 * 参数: a  数组指针
 * 参数: n  元素数目
 * 参数: x  要搜索的值
 *
 * 返回值:  若找到,返回该元素下标
 *          若找不到,返回-1
 */
/* ---------------------------------------------------------------*/
int bsearch_less(int *a,int n, int x)
{
    int m;
    int l = 0, r = n -1;
 
    while( r >= l )
    {
        m = (l+r)/2;
        if(a[m] == x)
            return ( (m-1) < 0 ? -1 : (m-1) );
        else if(a[m] < x)
            l = m+1;
        else
            r = m-1;
    }
    return r;
}
 
int main(int argc, char *argv[])
{
    int x;
    int arr[] = {8, 17, 26, 32, 40, 72, 87, 99};
    while(cin >> x)
    cout << bsearch_less(arr,8,x)+1 
        << " "
        << bsearch_more(arr,8,x)+1<< endl;
}



原文地址:http://www.orzace.com/binary-search/

你可能感兴趣的:(二分查找)