二分查找顾名思义,平分成两份查找(以网易云课堂 第六周作业为例,使用Python语言)
两点入门二分查找:
a. 猜测的值 = 中间值 =(low+high)/2 —— 二分的意义
b. 中间值<实际值,low = 中间值位置+1;中间值>实际值,high = 中间值-1
提供一个简易的代码模板(默认能够找到——如果不存在这个值也没必要查找了,提前使用in来判断是否存在):
def search(sequence, number, lower, upper):
if lower == upper:
assert number == sequence[upper] # 断言一定能找到,找不到则异常
return upper
else:
middle = (lower + upper)//2
if number > sequence[middle]:
return search(sequence, number, middle+1, upper)
else: # there has 2 situation--middle is not (middle-1)
return search(sequence,number,lower,middle)
一些实例:
1.定义一个 prime() 函数求整数 n 以内(不包括n)的所有素数(1不是素数),并返回一个按照升序排列的素数列表。使用递归来实现一个二分查找算法函数bi_search(),该函数实现检索任意一个整数在 prime() 函数生成的素数列表中位置(索引)的功能,并返回该位置的索引值,若该数不存在则返回 -1。
思考:
要查找位置并且返回索引值,一般解决方法很简单:使用循环判断 / 列表的index方法,但是要是列表很长东西很多,那么这样的从头开始循环的话则效率会很低,那么使用二分查找。
解:
说到递归函数,不得不再一次强调:一定要有终止条件返回;非终止条件的递归过程,调用自身前面一定要有return,否则返回的是None。
这道题中的终止条件便是:返回该位置的索引值,若该数不存在则返回 -1
def prime(n):
lst = []
for i in range(2, n):
for j in range(2, i):
if i % j == 0:
break
else:
lst.append(i)
return lst
n = 10 # assume n = 10
lst = prime(n)
x = raw_input("Enter num: ")
x = int(x) # raw_input() return a string
def bi_search(x, low, high):
guess = (low + high) / 2
print lst[guess], x
if (x == lst[guess]):
return guess
elif low > high:
return -1
elif lst[guess] > x:
print 'lst[guess] > x'
return bi_search(x, low, guess -1) # !!! take care
elif lst[guess] < x:
return bi_search(x, guess + 1, high) # !!! take care
再次通过同学ZHT的启发,简化下程序:首先利用Python 的 “in” 运算符判断数字是否在列表内(先决条件),再进行查找。
既然循环的先决条件是有x 存在于列表当中,所以必然就是能够找到的 x 的位置的,故终止条件只有一个:x 的位置
def bi_serach(lst,n):
lenth = len(lst)
while n in lst:
if n == lst[lenth / 2]:
return lenth / 2
elif n < lst[lenth / 2]:
lst = lst[:lenth / 2]
return bi_serach(lst,n)
else:
lst = lst[lenth / 2 + 1:]
return bi_serach(lst,n) + lenth /2 + 1 # after slicing you must do
return -1 # same indent with "While"
上述程序有一个需要注意的地方:切片操作,往后切片之后一定要注意下标的变化要还原,
之前提到过这样一个问题:“注意细节上的逻辑错误,在swap中,第二个参数不能够直接传mi,因为 mi 是在砍掉了 i 项之后的 nLst 中的位置 ≠ lst中的位置, 所以需要给mi 补上 i 才是 lst 中正确的位置。”
另外,使用循环实现二分查找:
同样需要找准终止条件,依然是:找到了 & 没找到的情况
逻辑是:没有找到就持续循环,找到了就return,故循环结束条件就是没有找到任何匹配position。
def bi_search(x, low, high):
while high >= low:
guess = (high + low) / 2
if x == lst[guess]:
return guess
elif lst[guess] > x:
high = guess - 1
else:
low = guess +1
return -1
当然,可以依照上述递归的方法也进行改装(循环的先决条件是,x 存在于列表当中),只需要将 while 条件(修改 while high >= low: )
while x in lst:
改变即可。