Python学习笔记:bisect模块实现二分搜索

一、简介

bisectPython 中的标准库(Standard Module),对有序列表提供支持,使得在插入新数据之后仍然保持有序。长列表的排序十分耗时,这个模块提供了良好的方法(bisect.insort)。模块使用基本的二分(bisection)算法。

Python 中可以利用 bisect 模块来实现二分搜索算法,在有序序列中查找或插入元素,该模块包含函数只有几个:

  • bisect:计算元素 x 在有序序列 a 中应该出现的位置(返回索引号)
  • bisect_left:返回左侧的索引位置(一般加1)
  • bisect_right:同 bisect 别名 返回右侧的索引位置
  • insort:将元素 x 插入有序序列 a 中
  • insort_left:将元素 x 插入有序序列 a 中(左侧)
  • insort_right:同 insort 别名(右侧)

前提:有序序列!!!

使用语法:

# 函数
bisect(a, x, lo, hi)

# 参数
# a:被查找的有序序列
# x:待插入的数值
# lo:在有序数列a中的搜索起点,默认值为0
# hi:在有序数列a中的搜索终点,默认值为len(a)

bisect 的返回值可作为 list.insert() 的参数。

二、实操

  • bisect函数
import bisect
a = [1, 3, 4, 5, 5, 5, 8, 10]
x = 5
bisect.bisect(a, x) # 6 应该插入的位置索引
bisect.bisect_right(a, x) # 6 应该插入的位置索引 同bisect
bisect.bisect_left(a, x) # 3 应该插入的位置索引
bisect.bisect(a, x, lo=7) # 7 若搜索起点超过x应该插入的位置 则返回搜索起点的索引
  • insort函数
import bisect
a = [1, 3, 4, 5, 5, 5, 8, 10]
x = 5.0 # 为方便展示 使用5.0来代替5
bisect.insort(a, x)
print(a)
# [1, 3, 4, 5, 5, 5, 5.0, 8, 10]
bisect.insort_right(a, x)
print(a)
# [1, 3, 4, 5, 5, 5, 5.0, 5.0, 8, 10]
bisect.insort_left(a, x)
print(a)
# [1, 3, 4, 5.0, 5, 5, 5, 5.0, 5.0, 8, 10]

bisectinsort 的区别在于前者只返回该插入的索引号,后者则直接将数值插入序列。

再来一个栗子。

import bisect
import random

mylist = list() 
# mylist = []
for i in range(10):
    num = random.randint(1, 100) # 随机整数
    index = bisect.bisect_left(mylist, num)
    bisect.insort_left(mylist, num)
    print('num:', str(num), 'index:', str(index), 'list:', mylist)
    
'''
num: 94 index: 0 list: [94]
num: 100 index: 1 list: [94, 100]
num: 32 index: 0 list: [32, 94, 100]
num: 46 index: 1 list: [32, 46, 94, 100]
num: 45 index: 1 list: [32, 45, 46, 94, 100]
num: 2 index: 0 list: [2, 32, 45, 46, 94, 100]
num: 76 index: 4 list: [2, 32, 45, 46, 76, 94, 100]
num: 85 index: 5 list: [2, 32, 45, 46, 76, 85, 94, 100]
num: 18 index: 1 list: [2, 18, 32, 45, 46, 76, 85, 94, 100]
num: 38 index: 3 list: [2, 18, 32, 38, 45, 46, 76, 85, 94, 100]
'''

mylist = list() 
# mylist = []
for i in range(10):
    num = random.randint(1, 100) # 随机整数
    index = bisect.bisect_right(mylist, num)
    bisect.insort_right(mylist, num)
    print('num:', str(num), 'index:', str(index), 'list:', mylist)

'''
num: 90 index: 0 list: [90]
num: 43 index: 0 list: [43, 90]
num: 3 index: 0 list: [3, 43, 90]
num: 25 index: 1 list: [3, 25, 43, 90]
num: 37 index: 2 list: [3, 25, 37, 43, 90]
num: 61 index: 4 list: [3, 25, 37, 43, 61, 90]
num: 24 index: 1 list: [3, 24, 25, 37, 43, 61, 90]
num: 66 index: 6 list: [3, 24, 25, 37, 43, 61, 66, 90]
num: 60 index: 5 list: [3, 24, 25, 37, 43, 60, 61, 66, 90]
num: 62 index: 7 list: [3, 24, 25, 37, 43, 60, 61, 62, 66, 90]
'''

三、源码

bisect 模块源码位于 Anaconda 安装目录下 D:\Anaconda\lib\bisect.py 可通过 bisect.__file__ 查看。

# bisect_right源码
def bisect_right(a, x, lo=0, hi=None):
    """Return the index where to insert item x in list a, assuming a is sorted.

    The return value i is such that all e in a[:i] have e <= x, and all e in
    a[i:] have e > x.  So if x already appears in the list, a.insert(x) will
    insert just after the rightmost x already there.

    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    return lo

bisect = bisect_right   # backward compatibility
# insort_right 源码
def insort_right(a, x, lo=0, hi=None):
    """Insert item x in list a, and keep it sorted assuming a is sorted.

    If x is already in a, insert it to the right of the rightmost x.

    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    a.insert(lo, x)

insort = insort_right   # backward compatibility

四、与list内置index()性能比较

bisect的源码可以看出,bisect模块内的函数使用二分查找的方式来加快寻找元素插入位置。对于插入元素,并保持列表有序 这样的操作要求我们可以结合list内置的index()函数与insert()函数来实现相同的效果。但由于index()函数是采用顺序查找的方式,故bisect模块的效率应高于list内置的index()函数。

index() 函数是从 startstop 顺序遍历的。

参考链接1:Python标准库 --- bisect

参考链接2:Python内置模块bisect

原文地址:https://www.cnblogs.com/hider/p/14711550.html