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

  • 相关阅读:
    沿着河边走,环城60公里
    与客家土楼的约会(叶剑英故居福建)
    与客家土楼的约会(增城河源)
    与客家土楼的约会(东源龙川)
    C#几个经常犯错误
    C# 获取当前路径方法
    C# Stopwatch与TimeSpan详解
    话说程序员成熟的标志
    C#开源项目介绍
    C#各种加密算法的研究
  • 原文地址:https://www.cnblogs.com/hider/p/14711550.html
Copyright © 2011-2022 走看看