二分查找算法

二分查找是一个很常用的算法,用于在有序数组中查找指定元素的索引

先来看版本一:

 1 def bin_search(lst, a):
 2     """
 3     二分查找函数
 4     lst: 序列,默认升序
 5     a: 要查找的值
 6     返回值: a的索引,找不到返回None
 7     """
 8     if a == lst[len(lst) // 2]:
 9         return len(lst) // 2
10     elif a < lst[len(lst) // 2]:
11         bin_search(lst[:len(lst) // 2], a)
12     else:
13         bin_search(lst[(len(lst)+1) // 2:], a)

这种方法存在以下几点错误:

(1)第11、13行递归时没有返回值,这是一个很低级的错误!

(2)此算法中对列表进行了切片,切片之后索引就变了,返回的索引就不是原列表的索引

版本二:

 1 def bin_search(lst, num, start=None, end=None):
 2     """
 3     二分查找
 4     :param lst: 列表
 5     :param num: 目标元素
 6     :param start: 查找的起始位置
 7     :param end: 查找的终止位置
 8     :return: 返回索引,找不到返回None
 9     """
10     start = start if start else 0
11     end = end if end else len(lst)-1
12     mid = (end - start)//2 + start
13     if start > end:
14         return None
15     elif num == lst[mid]:
16         return mid
17     elif num < lst[mid]:
18         bin_search(lst, num, start, mid-1)
19     else:
20         bin_search(lst, num, mid+1, end)

二分查找的效率是很高的,因为每调用一次查找范围就减半,因此只需要很少的次数就可以找到目标元素。

为了查看函数调用的次数,我写了一个简单的装饰器来输出函数递归调用的次数,完整代码如下:

 1 import time
 2 
 3 def call_num(func):
 4     """
 5     此装饰器函数用于计算函数调用次数
 6     :param func:
 7     :return:
 8     """
 9     num = 0
10 
11     def inner(*args, **kwargs):
12         nonlocal num
13         ret = func(*args, **kwargs)
14         num += 1
15         print("function called number:", num)
16         return ret
17     return inner
18 
19 
20 @call_num
21 def bin_search(lst, num, start=None, end=None):
22     """
23     二分查找
24     :param lst: 列表
25     :param num: 目标元素
26     :param start: 查找的起始位置
27     :param end: 查找的终止位置
28     :return: 返回索引,找不到返回None
29     """
30     start = start if start else 0
31     end = end if end else len(lst)-1
32     mid = (end - start)//2 + start
33     if start > end:
34         return None
35     elif num == lst[mid]:
36         return mid
37     elif num < lst[mid]:
38         bin_search(lst, num, start, mid-1)
39     else:
40         bin_search(lst, num, mid+1, end)
41 
42 l1 = []
43 
44 for i in range(100000000):
45     l1.append(i)
46 start_time = time.time()
47 print(bin_search(l1, 83374292))
48 end_time = time.time()
49 print(end_time-start_time)

执行结果

function called number: 1
function called number: 2
function called number: 3
function called number: 4
function called number: 5
function called number: 6
function called number: 7
function called number: 8
function called number: 9
function called number: 10
function called number: 11
function called number: 12
function called number: 13
function called number: 14
function called number: 15
function called number: 16
function called number: 17
function called number: 18
function called number: 19
function called number: 20
function called number: 21
function called number: 22
function called number: 23
function called number: 24
function called number: 25
83374292
0.008976936340332031

可见查找1亿条数据只需要0.009秒,函数调用了25次,是相当快的

原文地址:https://www.cnblogs.com/zzliu/p/10409138.html