二分搜索的魅力

浏览: 899

搜索或者查找是很重要的一类计算问题(另外一类比较重要的就是排序)。这应该是很多互联网笔试面试所热衷的一类问题,尤其是二分搜索,小编也多次面试过或者被面试过哦。

搜索,简而言之,就是判断一个对象k是否存在于list L中(用in可以实现)。通常情况下,如果存在,会求解k的索引(用L.index()可以实现)。

对于list相关的搜索函数,主要有以下几个:

elem in L:如果L中存在elem则为True否则为False;

L.index(elem):返回L中第一次出现elem的索引;如果不在L中,则返回error;

L.count(elem):返回elem出现在L中的次数;

min(L),max(L):返回L中的最小值或者最大值。

如果L是无序的,那么我们只能逐一搜索L中的对象,算法的复杂度正比于list的长度,最差为list的长度。这称为线性搜索,刚刚几个函数都是基于线性搜索,因为任何的list都可以使用这些函数来完成搜索。

如果L是有序的,那么便可以用二分搜索(二分查找,折半查找)来完成搜索。二分搜索比线性搜索的性能高的多。假设L是升序的,比较k与L的中间位置的对象的大小,分为三种情况:

A、k == L[middle],完成了搜索;

B、k < L[middle],需要搜索L的前半部分;

C、k > L[middle],需要搜索L的后半部分。

由此可见,当完成一次比较,要搜索的范围减少了1/2(对于线性搜索,每次比较后,要搜索的范围减少了常数1)。

在完成二分搜索时,我们保存两个变量left和right,L[left ... right]就是我们需要搜索的范围(初始化时left = 0,right = len(L)-1)。这时,k需要与搜索范围中间的对象,即索引为mid = (left + right)/2 的对象进行比较。比较后,我们搜索前半部分right = mid - 1 或者搜索后半部分 left = mid + 1。

二分搜索的Python代码如下:

# This is a function that returns the index of k if k is in L
# and returns -1, otherwise. The function assumes that L is
# sorted in ascending (non-decreasing) order and implements
# the binary search algorithm

def binarySearch(L, k):
   left = 0
   
right = len(L) - 1

   
# iterate while there is a sublist that needs to be searched
   
while left <= right:
       mid = (left + right) / 2  # index of the middle element
       # Comparisons and then adjusting the boundaries of
       # the sublist, if necessary
       
if L[mid] == k:
           return mid  # element is found at mid, so return this index
       
elif L[mid] < k:  # look for element in right half
           
left = mid + 1
       
elif L[mid] > k:  # look for element in the left half
           
right = mid - 1
   
return -1  # element is not found in the list

我们考察一下算法复杂度。由于每进行一次比较,搜索范围变为原来的1/2,假设L的长度为N,那么经过t次比较后,搜索范围变为N/2^(t)。当搜索范围变为1时,算法结束:N = 2^(t)。所以,算法复杂度为 t = log2(N)

最后,让我们通过简单的计算来感受二分搜索的高效与魅力吧!

假设有N个随机产生的1到N之间的整数,如何得到不重复的整数呢?请看一下运行时间差异:

import random
L = []
for i in range(50000):
   print i
   L.append(random.randint(1,50000))

count = 0
for e in range(1, 50001):
   if e in L:
       count = count + 1

在上述建立L并完成线性搜索判断的过程中:建立L耗时0.78s,线性搜索判断的耗时为29.833s。

下面看一下二分搜索:

import random
L = []
for i in range(50000):
   L.append(random.randint(1,50000))

L.sort()

count = 0
for e in range(1, 50001):
   if binarySearch(L, e) >= 0:
       count = count + 1
print count

在上述建立L并完成二分搜索判断的过程中:建立L耗时0.79s,排序耗时0.031s,二分搜索判断耗时0.169s。

你发现耗时的巨大差距了吗? 你看到二分搜索的巨大魅力了吗?

推荐 2
本文由 曹金龙 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册