Binary Search 《二分查找》 1


二分查找

By lovro

TS ekse

原文地址

        二分查找时计算机科学中一个基础的算法。为了研究它,我们要先建立一个理论上的支柱,然后正确的用它来实现这个算法同时避免每个人都讨论过的离奇的差一错误。

在一个已排序序列中查找一个值

        二分查找最简单的形式,它用于快速的在已排序序列中查找一个值(现在把这个序列认为是一个普通的数组)。我们把要查找的值叫做目标值(target作为区别。二分查找始终维护一个原始序列的包含目标值的子序列。这个子序列叫做搜索区域(search space。最开始搜索区域是整个序列。每一步,这个算法都会用查找区域的中间值和目标值做比较。由于这个比较和序列是已排序的,这样就排除了一半的查找区域。重复的这样做,它最后会剩下一个查找区域只包含一个元素,就是目标值

        例如,考虑下边这个已排序的递增整数序列,假如说我们要查找55

0

5

13

19

22

41

55

68

72

81

98

        我们想要知道的是目标值在序列中的位置所以我们将用序号来表示查找区域。最开始,查找区域包括111。因为查找区域是一个区间,存储两个数,小序号和大的序号。如上所描述的,我们就可以选择中间值,中间值就是序号为6的那个数(111的中间点):值为41,但是它要比我们要找的目标值要小。现在我们可以断定不仅序号为6所代表的数的值要比目标值小,而且在15之间的数都不可能是目标值,因为这个区域所有的值都要比41要小。这样就把查找区域缩小到了711

55

68

72

81

98

        用类似的方式处理,我们就去掉了第二个区域留下了:

55

68

        根据我们在遇到奇数元素个数时选择中间值的方式,我们将在就找到 55 或者在去掉 68 只剩一个元素时找到它。不管哪种方法,我们都知道我们要找的值在序列中的位置是 7
如果目标值不在这个序列中,二分查找将清空查找区域。这个条件很好检查和处理。下面就是根据描述得到的代码:

 

复杂度

 

        因为每次比较二分查找都是使用一般的查找区域,我们可以断言并轻松的证明二分查找不会使用超过(O表达式)O(log N)次比较久会找到目标值。

这个算法是一个很慢的增长函数。如果你不知道二分查找的效率有多高,考虑下边从一个有上百万姓名的电话本中查找一个名字。二分查找可         以在21次比较内系统的找到你想要的名字。如果你可以获得全球所有人已排序的名字序列,那么你可以在35次比较内找到你想要的人。这好像现在不可行和没什么用,但是我们将马上证明它。

注意这个我们可以随意访问到这个序列中的值。试着在例如像链表的容器上就会没有什么效果,却用线性查找代替还更好。

标准库中的二分查找

        C++的标准类库实现了二分查找在lower_boundupper_boundbinary_searchequal_range,根据你实际想要做的。Java也有一个内置数组。数组的二分查找和.NET框架下有Array.BinarySearch

        你最好在可能的情况下使用库函数,因为,就像你知道的,自己实现一个二分查找可能会有错误。

除了数组:离散的二分查找

        我们从这里开始抽象的讨论二分查找。一个序列(数组)只是映射了一个整数(索引)和对应的值的函数。然后,没有理由来限制我们把二分查找运用到实际的序列中。实际上,我们可以把上边讨论的算法使用在任何有一个整数集合定义域的线性函数f。唯一的不同是我们用一个判等函数来查找这个数组:我们要做的是找到x满足f(x)等于目标值。查找区域就是一个更普通的定义域的一个子集,同时我们要找的目标值就在对应的定义域中。二分查找的力量现在开始显现出来:我们不仅最多只需O(log N)次比较来找到目标值,而且我们不用多次的求这个函数的值。更进一步,我们不受实际数量例如可用内存,同数组一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值