Python 标准库之 bisect 二分查找

Python 标准库之 bisect 二分查找


一、二分查找法🎏

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法
二分查找法的时间复杂度为:O(㏒n)

特点

  • 仅当 数组或列表 是 有序 的时候,二分查找法才管用。列如:通讯录(微信、QQ等)里的联系人是按字母顺序排序的,因此可以使用二分查找法来查找联系人
  • 二分查找『 O(㏒n) 』的速度比 简单查找『 O(n) 』快,搜寻的元素越多,二分法就越快
  • 另一方面,简单查找虽然慢,但代码编写起来更容易,且出现bug的几率会更小

简单查找

就是将 数组一个一个查找

def simple_search(array: list):
    """
    简单查找示例
    :param array: 数组列表
    """
    target = input("数组范围 1~1000,请输入需要查找的值:")
    if target.isdigit():
        target = int(target)
    else:
        raise TypeError("请输入整数")
    for i in array:
        if i == target:
            print(f"查找了{i}次找到目标值 {target}")
            break
    else:
        print("需要查找的值不存在")


arr = list(range(1, 1001))
simple_search(arr)

二分查找

二分原理:将 数组分为 大致相等的两份,取中间值(中位数)进行比较,如果中间值等于 目标值,条件达成、查找成功,如果 目标值 小于 中间值,则在 数组左半部分 继续以这种方式搜索,如果 目标值 大于 中间值,则在数组右边部分搜索。

def binary_search(array: list):
    """
    二分查找 while
    :param array: 数组列表
    """
    target = input("数组范围 1~1000,请输入需要查找的值:")
    if target.isdigit():
        target = int(target)
    else:
        raise TypeError("请输入整数")
    low, high = 0, len(array) - 1     # 数组开头索引(默认为 0), 数组结尾索引
    count = 0                         # 寻找次数计数器
    while low <= high:                # 如果结尾索引大于等于开头索引 则不断循环
        median = (low + high) // 2
        count += 1
        if array[median] > target:    # 如果目标值比中间数小,位于中间数左边,更改high索引
            high = median - 1
        elif array[median] < target:  # 如果目标值比中间数大,位于中间数右边,更改high索引
            low = median + 1
        else:
            print(f"查找了{count}次找到目标值{target}")
            break


arr = list(range(1, 1001))
binary_search(arr)

二、bisect 下函数

bisect 下的函数都有四个参数,『 a,x,lo=0,hi=len(a) 』
其中 a 和 x 为位置参数,a 传入的是 需要搜寻的数组,x 传入的是 目标值
lo 和 hi 为位置参数,lo 是 low 的缩写,代表数组搜寻范围中最小数的索引(查询范围开头索引),默认值为 0,即从第一个值开始查找;hi 是 high 的缩写,代表数组搜寻范围中最大数的索引(查询范围结尾索引),默认值为数组的长度

默认情况下整个 列表/数组 都会被使用

1) bisect.bisect(a, x, lo=0, hi=len(a))

返回 x 在 a 中 合适的插入点索引 以维持数组的有序, 如果 x 在 a 中已存在,那么返回的插入点是 a 中所有 已存在元素 x 的右侧

注意:此函数是返回 插入点索引,并不是真的把数值 插入到列表数组中

arr = [1, 2, 3, 5]
arr_1 = [1, 2, 3, 4, 4, 4, 5]
print(bisect.bisect(arr, 4))            # 无重复值列表数组
print(bisect.bisect(arr_1, 4))          # 插入点索引 在所有已存在值的 右侧
print(bisect.bisect_right(arr_1, 4))    # 插入点索引 在所有已存在值的 右侧
3
6
6

2) bisect.bisect_right(a, x, lo=0, hi=len(a))

源码👇
在这里插入图片描述
可以看到,调用 bisect 实质上就是在调用 bisect_right,使用方法是相同的

3) bisect.bisect_left(a, x, lo=0, hi=len(a))

bisect_left 类似于 bisect_right / bisect ,但是不同在于 如果 x 在 a 中已存在,那么返回的插入点是 a 中所有 已存在元素 x 的左侧

arr = [1, 2, 3, 5]
arr_1 = [1, 2, 3, 4, 4, 4, 5]
print(bisect.bisect(arr, 4))            # 无重复值列表数组
print(bisect.bisect(arr_1, 4))          # 插入点索引 在所有已存在值的 右侧
print(bisect.bisect_right(arr_1, 4))    # 插入点索引 在所有已存在值的 右侧
print(bisect.bisect_left(arr_1, 4))     # 插入点索引 在所有已存在值的 左侧
3
6
6
3

4) bisect.insort(a, x, lo=0, hi=len(a))

将 x 插入到一个 有序数组 a 里,并维持其有序, 如果 x 在 a 中已存在,那么将插入到 a 中所有 已存在元素 x 的右侧

arr = [1, 2, 3, 4, 4, 4, 5]
bisect.insort(arr, 6)         # 插入数值,若已存在则在所有已存在值的右侧插入
print(arr)
bisect.insort_right(arr, 5)   # 插入数值在 所有已存在值的 右侧
print(arr)
[1, 2, 3, 4, 4, 4, 5, 6]
[1, 2, 3, 4, 4, 4, 5, 5, 6]

注意:搜索查询时间复杂度为:O(log n) ,而插入却是 O(n)
没有返回值


5) bisect.insort_right(a, x, lo=0, hi=len(a))

源码👇
在这里插入图片描述
可以看到,调用 insort 实质上就是在调用 insort_right,使用方法是相同的

6) bisect.insort_left(a, x, lo=0, hi=len(a))

将 x 插入到一个有序序列 a 里,并维持其有序。如果 a 有序的话,这相当于 a.insert(bisect.bisect_left(a, x, lo, hi), x)。要注意搜索是 O(log n) 的,插入却是 O(n) 的。

arr = [1, 2, 3, 4, 4, 4, 5]
bisect.insort(arr, 6)         # 插入数值,若已存在则在所有已存在值的右侧插入
print(arr)
bisect.insort_right(arr, 4)   # 插入数值在 所有已存在值的 右侧
print(arr)
bisect.insort_left(arr, 5)    # 插入数值在 所有已存在值的 左侧
print(arr)
[1, 2, 3, 4, 4, 4, 5, 6]
[1, 2, 3, 4, 4, 4, 4, 5, 6]
[1, 2, 3, 4, 4, 4, 4, 5, 5, 6]

注意:搜索查询时间复杂度为:O(log n) ,而插入却是 O(n)
没有返回值


参考资料💌


相关博客😏

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值