什么是二分查找?

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。

要求:元素必须按顺序排列,升序/降序 都可

基本思想是将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x

代码示例

def bin_search(data_list, val):    
    low = 0                         # 最小数下标    
    high = len(data_list) - 1       # 最大数下标    
    while low <= high:        
        mid = (low + high) // 2     # 中间数下标 //向下取整        
        if data_list[mid] == val:   # 如果中间数下标等于val, 返回            
            return mid        
        elif data_list[mid] > val:  # 如果val在中间数左边, 移动high下标            
            high = mid - 1        
        else:                       # 如果val在中间数右边, 移动low下标            
            low = mid + 1    
    return # val不存在, 返回None
ret = bin_search(list(range(1, 10)), 3)
print(ret)

 

举例说明:

data_list = [1, 3, 5, 7, 9, 11, 13]   val = 3

最小下标low=0, 最大下标high=len(data_list)-1==6,中间数下标mid=(low+high)//2==3,列表分为两部分,左列表==[1, 3, 5]  右列表==[9, 11, 13], 中间数mid_num=data_list[3]==7

val != mid_num且val < mid_num,val 在data_list左半部分

所以最大下标high=mid-1==2,左半部分列表==[1, 3, 5]

mid=(low+high)//2==1

中间数mid_num=data_list[1]==3

val == mid_num

查找完成

 

bisect--数组二分查找算法

bisect 模块包含两个主要函数( bisect 和 insort),它们内部利用二分查找算法,分别用于在有序序列中查找元素与插入元素。

  • 查找: bisect(array, item)  
  • 插入: insort(array,item)

bisect有三个方法

  • bisect.bisect()
  • bisect.bisect_left()
  • bisect.bisect_right()
  • 貌似bisect()与bisect_right()作用相同

语法:bisect.bisect_left(a,x, lo=0, hi=len(a)) 

说明:a=序列变量,x=查找的值,lo=检索的起始位置,hi=检索的结束位置 lo、hi可以忽略,忽略后使用整个列表

bisect.bisect_left函数返回这样的位置:位置左侧值都小于x, 位置右侧大于等于

bisect.bisect_right函数返回这样的位置:位置左侧值都小于等于x, 位置右侧大于

bisect_left与bisect_right 并不添加至序列 只是返回应该排序的位置

 

当没有相同元素时,三个方法返回值相等

tuple_1 = (1, 3, 5, 7, 9)
print(bisect.bisect(tuple_1, 4))
print(bisect.bisect_left(tuple_1, 4))
print(bisect.bisect_right(tuple_1, 4))
#输出
2
2
2

 

当有相同元素时:

tuple_1 = (1, 3, 5, 5, 7, 9)
print(bisect.bisect(tuple_1, 5)) 
print(bisect.bisect_left(tuple_1, 5))
print(bisect.bisect_right(tuple_1, 5))
#输出
4
2
4

 

对比查询效率

def test1():
index=2**25
ls=[i for i in range(2**27)]
a1 = bisect.bisect(ls,index)
print(f"a1:{a1}")

t = time.perf_counter()
test1()
print(f'二分查找bisect:{time.perf_counter() - t:.8f}s')

def test2():
    index=2**25
    ls=[i for i in range(2**27)]
    a2 = ls.index(index)
    print(f"a2:{a2}")
t = time.perf_counter()
test2()
print(f'常规list.index:{time.perf_counter() - t:.8f}s')

输出:

#输出

a1:33554433
二分查找bisect:25.99148979s
a2:33554432
常规list.index:32.69605091s

 

insort和bisect一样对应三个方法

  • bisect.insort()
  • bisect.insort_left()
  • bisect.insort_right()

当插入不存在的元素时,三个方法作用相同

当插入存在的元素时,判断插入该元素的前面(insort, insort_left)还是后面(insort_right)