先理解一下什么是二分(折半)算法?先来看个有趣的例子:

一间很大的教室如果老师要点人数,有什么好方法吗?一个一个点,2个2个点其实这都是算法,如果这个教室有10000人,你怎么数呢,有一个很好的方法就是让所有同学都站起来,每个人代表数字1,然后两个人商量好一个人取得对方的数字相加,另外一个人就坐下去,以此类推,每一次都会有原来一半的人坐下去,这样不用几次剩下最后一个人他的数字就是总人数了。10000人理论上214次方就远远超过了,也就是14次就能数万了。

不过这个让人实现起来比较困难,因为有的人会犯错,让计算机去做反而更容易。


二分(折半)查找

假设要在电话簿中找一个名字以K打头的人,(现在谁还用电话簿!)可以从头开始翻页,直到进入以K打头的部分。但你很可能不这样做,而是从中间开始,因为你知道以K打头的名字在电话簿中间。 

image.png

又假设要在英文字典中找一个以O打头的单词,你也将从中间附近开始。

现在假设你登录某个网站。当你这样做时,网站必须核实你是否有其网站的账户,因此必须在其数据库中查找你的用户名。如果你的用户名为karlmageddon,该网站可从以A打头的部分开始查找,但更合乎逻辑的做法是从中间开始查找。

这是一个查找问题,在前述所有情况下,都可使用同一种算法来解决问题,这种算法就是二分查找。

二分查找是一种算法,其输入是一个有序的元素列表(必须有序的原因稍后解释)。如果要查找的元素包含在列表中,二分查找返回其位置;否则返回null。

下面的示例说明了二分查找的工作原理。我随便想一个1~100的数字。

image.png


你的目标是以最少的次数猜到这个数字。你每次猜测后,我会说小了、大了或对了。 假设你从1开始依次往上猜,猜测过程会是这样。 

image.png

image.png

糟糕的猜数方法


这是简单查找,更准确的说法是傻找。每次猜测都只能排除一个数字。如果我想的数字是99,你得猜99次才能猜到! 


更佳的查找方式 

下面是一种更佳的猜法。从 50 开始。

image.png


小了,但排除了一半的数字!至此,你知道1~50都小了。接下来,你猜75。 

大了,那余下的数字又排除了一半!使用二分查找时,你猜测的是中间的数字,从而每次都将余下的数字排除一半。接下来,你猜63(50和75中间的数字)。 

image.png


这就是二分查找算法!每次猜测排除的数字个数如下。

image.png

使用二分查找时,每次都排除一半的数字不管我心里想的是哪个数字,你在7次之内都能猜到,因为每次猜测都将排除很多数字! 

假设你要在英文字典中查找一个单词,而该字典包含240 000个单词,你认为每种查找最多需要多少步? 

image.png

说 明:

仅当列表是有序的时候,二分查找才管用。例如,电话簿中的名字是按字母顺序排列的,因此可以使用二分查找来查找名字。如果名字不是按顺序排列的,结果将如何呢?

其实二分查找算法也是一种分治策略的算法。


点我查看Scratch模拟二分查找在列表中查询某个数字(按空格键非递归实现  按绿旗递归实现)



Python实现二分查找

# 二分查找
# 一定要是有序的列表(升序或者降序)
# 这里把列表处理成升序排列
# 每次把列表分成一半再查找

# 非递归方法
def binarry_search(data_list, target):
   '''
   非递归
   
:param data_list: 传入有序列表
   
:param target:    要查找的目标数
   
:return: 返回值
   '''
   
# low和high表示列表中的下标 第一个和最后一个
   
low = 0
   
high = len(data_list) - 1  
   
count = 1  # 计查找的次数
   
while low <= high:
       mid = (low + high) // 2  # ‘//’表示向下取整 例如9//2等于4
       
if target == data_list[mid]:
           return '一共找了%d次,找到了目标数:%d' % (count, target)
       elif target > data_list[mid]:
           low = mid + 1
       
else:
           high = mid - 1
       
count += 1
   
return '一共找了%d次,没有找到目标数' % count

# 递归方法
def binary_search2(alist, target):
   if len(alist) == 0:
       return '查询的数字{}不在此列表中'.format(target)
   else:
       midpoint = len(alist) // 2

       if alist[midpoint] == target:
           return '在查询的列表中找到目标数字{}'.format(target)
       else:
           if target < alist[midpoint]:
               return binary_search(alist[0:midpoint], target)
           else:
               return binary_search(alist[midpoint + 1:], target)
                       
if __name__ == '__main__':
   data = [5, 15, 16, 18, 25, 33, 89, 100, 117, 139]
   print(data_list)
   print(binarry_search(data_list, 5))   # 递归调用
   print(binarry_search2(data_list, 5))  # 非递归调用




0 评论 最近

没有评论!