算法之旅——二分查找

        在介绍二分查找之前,先给大家讲一个故事吧:一个女生背着满书包的书进入图书馆,叮叮叮,图书馆的报警器响了,女孩赶紧把书从书包倒出来,准备一本一本的验证,看是哪本书有问题,一旁扫地的阿姨看不下去了,过来把书分成了两挪,先检查第一挪,叮叮叮,报警器响了,说明这一挪有问题,又把这一挪分成两挪,先检查其中一挪,要是哪一挪响了,就把这一挪继续分成两挪,继续检查,不到三回合,大妈就把有问题的哪本书找出了了得意。大妈用鄙视的眼神看着那女生,仿佛在说连O(n)跟O(log n)都分不清楚。好了,故事讲完了,看来扫地的人都很厉害哦,不禁想起了天龙八部里的扫地僧~,咳咳,又扯远了了。

       这里介绍的二分查找法跟大妈找书的方法如出一辙,二分查找又称折半查找,优点是比较的次数少,查找速度快,平均性能好,时间复杂度为O(log n);其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。其主要思想如下:对一个有序的数组arrary[n],需要查找一个数key,看key是否在该数组中。

(1)设置两个变量left和right, 使left=0,right=n-1,先取数组的中间的那个数arrary[middle] ,(tips:middle=left+(right-right)>>1,如此算middle值,可以防止溢出),将key和arrary[middle]比较,若key小于arrary[middle],说明key处于数组左边部分,令right=middle-1;若key大于arrary[middle],说明key处于数组右边部分,令right=middle+1

(2)根据新的left或right,取新数组的中间值middle=left+(right-right)>>1,将key和新的middle值比较,若key小于arrary[middle],说明key在数组左边部分,令right=middle-1;若key大于arrary[middle],说明key在数组右边部分,令right=middle+1

(3)重复步骤(2),直至left等于right,在这个过程中没找到了key值,就返回其在数组中的下标,没找到key就返回-1

下面给出一个具体的实例,看二分查找是怎样工作的

设已知数组为arrary[]={18,29,36,38,71,89,110,129,160},查找的值value=129

数值

18    

29   

36   

38   

71   

89   

110  

129  

160  

下标

0

1

2

3

4

5

6

7

8

首先令left=0,right=8,则middle=(8-0)/2= 4; 将value=129与arrary[middle]=arrary[4]=71比较,显然value大于arrary[middle],那么就令left=middle+1=4+1=5,新的middle值为left+(right-right)>>1=5+(8-5)/2=6, 将value与新的arrary[middle]=arrary[6]=110比较,value又大于arrary[middle],继续令left=middle+1=6+1=7,新的middle值为left+(right-right)>>1=7+(8-7)/2=7,再将value与arrary[7]=129比较,哈哈,value==arrary[7],终于找到了,看来二分法查找数据还是蛮快的嘛,仅仅用了三趟就找到了。

        由上面的查找过程可以看出,二分法比常规的顺序查找快多了,节省了很多时间,尤其是在数据量很大的时候,二分法更能显出其优势。不过使用二分法查找有一个不好的地方就是待查找的数组必须是有序的,可是是升序,也可以是降序,这就导致查找之前多了一个工作量——对数组排序,关于数组排序的算法,可以参考我的上一篇博客《算法之旅——快速排序

好了,下面给出一份二分查找的参考代码(以下代码均已编译通过,正常运行)

//description:二分查找算法
//author:hust_luojun
//data:2014-7-16

#include <iostream>

using namespace std;

int main()
{
    int binary_search(int arrary[],int length,int key);
    int arrary[] = {7,18,29,36,38,71,89,110,129,160,222,225,265,360,361,721};
    int key = 160;
    int pos = binary_search(arrary,16,key);
    for(int i=0;i<16;i++)
    {
        cout<<arrary[i]<<"  ";
    }
    cout<<endl;
    cout<<"the number "<<key<<" that searched is in position: "<<pos<<endl;
    return 0;
}

int binary_search(int arrary[],int length,int key)
{

   if(arrary == NULL || length <= 0)        //错误处理
        {
            cout<<"error! the arrary is incorrect"<<endl;
            return -1;
        }
   else
    {
        int left = 0;
        int right = length - 1;

        while(left <= right)
            {
                 int middle;
                 int pos;

                middle = left + ((right - left)>>1); //建议不要写成middle=(left+right)/2,防止数组很大时,溢出

                if(key < arrary[middle])        //查找的数值key小于数组中间值
                    {
                        right = middle - 1;
                    }
                else if(key > arrary[middle])   //查找的数值key大于数组中间值
                        {
                            left = middle +1;
                        }
                    else if(key == arrary[middle])  //查找的数值key等于数组中间值
                        {
                            return middle;          //返回key在数组中的位置,即数组下标
                        }

            }

       return -1;       //若数值key不在此数组中,返回-1
    }

}
附上程序运行的结果:


/*****码字不易,转载请注明出处*****/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值