二分查找及其Python实现【算法图解】
1. 引出问题
做一个游戏,我随便想一个整数数字,这个数字在【1-100】范围内,你的目标是以最少的次数猜到这个数字,你会怎么做?
注意:在你每次猜测后,我都会提示猜大了(猜小了/猜对了)
2. 简单查找
简单查找的过程看起来很笨,类似于这样(假设我想的数字是68):
你:1
我:猜小了
你:2
我:猜小了
你:3
我:猜小了
…
你:60
我:猜小了
你:woc,我不猜了,难受!!!
我:嘻嘻
显然,简单查找每次只能排除一个数字,如果我想的数字是100,你必须猜测100次才可以猜到。
3. 二分查找(折半查找)
二分查找的过程简单多了,类似于这样(假设我想的数字是68):
你:floor((100+1)/2)=50
我:猜小了
(排除了【1-50】的数字,还剩下【51-100】)
你:floor((51+100)/2)=75
我:猜大了
(排除了【75-100】,还剩下【51-74】)
你:floor((51+74)/2)=62
我:猜小了
(排除了【51-62】,还剩下【63-74】)
你:floor((63+74)/2)=68
我:猜对了!
你每次猜测都会排除约一半的数字,迅速把范围缩小,因此只用了4步就猜对了!!!
这就是二分查找,在本游戏中,每次猜测剩余的的数字个数如下:
在最坏情况下,我们只需要需要猜测7次就可以得到结果(而简单查找需要100次)。
4. 运行时间对比
简单查找 | 二分查找 |
---|---|
100个元素:最多猜测100次 | 100个元素:最多猜测7次 |
4000000000个元素:最多猜测4000000000次 | 4000000000个元素:最多猜测32次 |
n个元素:运行时间(猜测次数)O(n) | n个元素:运行时间(猜测次数)O(log n) |
可以看到,简单查找的时间是线性时间,而二分查找的时间是对数时间(底数为2),随着元素数量增加,二分查找优势愈加明显。
准确来讲,二分查找的猜测次数是ceil(log n)
,当有100个元素时,ceil(log 100)=7
,这里用大O表示法忽略了小数
5. 二分查找的Python实现
键入以下代码并运行:
## 二分查找的函数binarySearch
## 输入:
## orderedList:有序的列表
## item:需要寻找的值
## 输出:
## mid:列表orderedList中等于item的元素对应的位置索引
## None:如果不存在与item相等的元素,返回空
def binarySearch(orderedList,item):
low=0 # low:存储剩余范围的最小索引(下边界)
high=len(orderedList)-1 # high:存储剩余范围的最大索引(上边界)
while low<=high:
mid=int((low+high)/2) # mid:计算剩余范围的中间索引
guess=orderedList[mid] # guess:剩余范围的中间值即为猜测的数值
if guess==item: # 如果猜对了,那么返回索引
return mid
if guess>item: # 如果猜的值大了,那么将剩余范围的上边界更新为中间位置
high=mid-1
else: # 如果猜的值小了,那么将剩余范围的下边界更新为中间位置
low=mid+1
return None # 如果不存在对应值,返回空
## 测试用例,第一个3存在,结果应该为1,第二个-1不存在,结果为None
myList=[1,3,5,7,9]
print(binarySearch(myList,3))
print(binarySearch(myList,-1))
这里面的注释是比较清楚的,因此就不再过多赘述,运行结果如下:
6. 总结
-
二分查找的速度比简单查找快得多。
-
O(log n)比O(n)快,且随着元素规模的增加,前者比后者快得多。
-
注意:二分查找的基础是有序列表,因此无序列表不能应用二分查找。