zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
    数据库中间件 ShardingJDBC 源码分析 —— SQL 解析(三)之查询SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(六)之删除SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(五)之更新SQL
    消息队列中间件 RocketMQ 源码分析 —— Message 存储
    源码圈 300 胖友的书单整理
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(一)分库分表配置
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(四)之插入SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(二)之分库分表路由
    C#中Math类的用法
  • 原文地址:https://www.cnblogs.com/hider/p/14711550.html
Copyright © 2011-2022 走看看