二分查找

1.猜数

(以下故事纯属虚构,如有雷同,不胜荣幸)

小海:“小涵,你现在心里想一个1000以内的数,信不信我最多只要问你十个问题,我就能猜出这个数是什么?”

小涵:“真的么? 我不信。”

小海:“不信? 那就来试一试怎么样?”

小涵:“哟,还来劲了,来啊。”

小海:“那现在给你十秒钟时间想这个数字。”

小涵:“.......(沉默)”

小海:“10,9,8,...”

小涵:“别点啦,我已经想好了。”

小海:“这么快啊,不多想会儿? 不然等下你又说我作弊。”

小涵:“我.......(-_-||),快点开始吧。”

小海:“OK,那现在开始了,你想的数比500大吗?”

小涵:“嗯,对,比500大。”

小海:“嗯,那好,这个数比750大吗?”

小涵:“没有。”

小海:“比625大吗?”

小涵:“没有呢。”

小海:“比562大吗?”

小涵:“没有,你怎么老是问同样的问题,能不能换个有趣一点的。”

小海:“这个问题怎么就无趣了,继续,这个数比531大吗?”

小涵:“没有,你不能换个有新意的问法吗? emmmmm”

小海:“奥,要有新意啊,那这个数比428加87更大吗?”

小涵:“这。。。428加87.......,我...,等我算一下。”

小涵:“428加87等于515,比515更大,你还是用原来的问法吧。-_—”

小海:“哈哈哈哈哈哈,你不是要有趣的问法吗,那这个数比523大吗?”

小涵:“这个叫有趣...(小声),额,没有,比523更小。”

小海:“昂,就要接近真像了,这个数比519更大吗?”

小涵:“嗯嗯,比他更大。”

小海:“这个数有比521更大吗?”

小涵:“不会做真的能猜出来吧。。。(心想),嗯,没。”

小海:“这个数有比520更大吗?”

小涵:“没有。(假装镇定)”

小海:“奥,这样啊。这个数是520”

(场面突然升温)

小涵:“嗯呢,(3秒的沉默),520。”

(小海凝望着小涵)

小海:“怎么样,我厉害吧。”

小涵:“(石化4秒)?????????????????? 我 ..... , 你....(老娘话都到这儿了,你........)”

小海:“嘿嘿,想学吗??”

小涵:“(死亡微笑)再见。”

(小涵转身走了)

小海:“嗯? 喂! 不学就不学呗,干嘛走啊??? 我是说错了什么吗???(默默追上了小涵)”

......

.....

....

...


2.情侣的毁灭能手:二分

不管你信不信,上面小海跟小涵玩的小游戏就是二分查找的核心。

在这个游戏中,小涵心中想的数其实就是我们要在有序数字序列中查找的x,而小海说的1000内任意数字其实就是序列的长度。

这个算法的核心思想就是不断地用当前序列区间中点的值value与x比较,若x比中间值value大,则在当前区间的右半区间继续查找x,否则在当前区间的左半区间查找,直到x被找出来。

每一次操作,都能减少当前区间一半的查找量。 这样也就使得查找的效率大大提高。

举个栗子:

假设有序数字序列a为: 1 5 14 21 29 31 34 35

需要查找的数x为: 14

假设该数字序列下标从1开始,则序列区间为[1,8]

设l为区间左端点,r为区间右端点。

初始情况l = 1, r = 8,中点mid = (l+r)/ 2 = (1+8)/ 2 = 4 (C语言中用int直接取整)

将中点的值a[mid] = a[4] = 21 与 x = 14进行对比

x < a[mid] 成立

则在当前区间的左半边进行查找,令l = l = 1,r = mid-1 = 3

那现在的区间是[1,3]

序列为:1 5 14

重复上述过程

mid = (l+r) / 2 = (1+3) / 2 = 2

a[mid] = 5

x = 14

x < a[mid] 不成立

x > a[mid] 成立

所以令l = mid + 1 = 3, r = r = 3

新缩小后的区间为[3,3]

序列为:14

mid = (l+r) / 2 = (3+3)/2 = 3

a[mid] = 14

x = 14

x < a[mid] 不成立

x > a[mid] 不成立

x == a[mid] 成立

令position = mid = 3

所以最后寻找得到x的位置是3


3.C语言代码

#include<stdio.h>

int main()
{
    int a[100],i,x,n;
    scanf("%d",&n);
    for(i = 1; i <= n; i++)
        scanf("%d",&a[i]);
    scanf("%d",&x);
    
    int l = 1,r = n,mid,pos = -1;
    
    do{
        mid = (l+r)/2;    //序列下标中点计算
        if(x < a[mid])
            r = mid-1;   // 缩小区间,在当前区间左边查找
        else if(x > a[mid])
            l = mid+1;   //在当前区间右边查找
        else {          //找到就退出循环
            pos = mid;
            break;
        }
    }while(l <= r);
    
    if(pos == -1)printf("Not find!\n");      //pos初始化的时候是-1,如果该变量没有被改变过,则序列中没有这个元素
    else printf("position is %d\n",pos);
    return 0;
}

该程序的输入是序列长度n,有序序列a,以及待查元素x。

posted @ 2019-07-28 21:18  君焰w  阅读(152)  评论(0编辑  收藏  举报