手动实现

假如有一个有序表nums,怎么样在nums里找到某个值的位置呢?没错,就是nums.index(k),哈哈哈哈哈哈哈……

假如nums很长很长,那就要祭出二分查找了

def binary_search(nums: List[int], k: int) -> int:
if not nums:
raise ValueError('list is empty') left, right = 0, len(nums)-1
while left <= right:
mid = (right + left) // 2
if k < nums[mid]:
right = mid - 1
elif k > nums[mid]:
left = mid + 1
else: # if k == nums[mid]:
return mid
raise ValueError(f'{k} is not in list')

计算mid的公式为(right + left) / 2,动态语言随便写,但是在静态语言中建议写成left + (right - left) / 2,这样可以防止rightleft都很大时,(right + left)溢出。

使用bisect

二分查找可以给查找加速,但是每次写这么一段代码也够心烦。其实Python有这么一个库:bisect 这个库实现了二分查找和二分插入。

bisect的方法数量不多,参考官方文档给出的说明:

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

    在 a 中找到 x 合适的插入点以维持有序。参数 lo 和 hi 可以被用于确定需要考虑的子集;默认情况下整个列表都会被使用。如果 x 已经在 a 里存在,那么插入点会在已存在元素之前(也就是左边)。如果 a 是列表(list)的话,返回值是可以被放在 list.insert() 的第一个参数的。

    返回的插入点 i 可以将数组 a 分成两部分。左侧是 all(val < x for val in a[lo:i]) ,右侧是 all(val >= x for val in a[i:hi]) 。

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

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

    类似于 bisect_left(),但是返回的插入点是 a 中已存在元素 x 的右侧。

    返回的插入点 i 可以将数组 a 分成两部分。左侧是 all(val <= x for val in a[lo:i]),右侧是 all(val > x for val in a[i:hi]) for the right side。

  • 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) 的。

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

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

    类似于 insort_left(),但是把 x 插入到 a 中已存在元素 x 的右侧。

参见: SortedCollection recipe 使用 bisect 构造了一个功能完整的集合类,提供了直接的搜索方法和对用于搜索的 key 方法的支持。所有用于搜索的键都是预先计算的,以避免在搜索时对 key 方法的不必要调用。

搜索有序列表

上面的 bisect() 函数对于找到插入点是有用的,但在一般的搜索任务中可能会有点尴尬。下面 5 个函数展示了如何将其转变成有序列表中的标准查找函数

def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x:
return i
raise ValueError def find_lt(a, x):
'Find rightmost value less than x'
i = bisect_left(a, x)
if i:
return a[i-1]
raise ValueError def find_le(a, x):
'Find rightmost value less than or equal to x'
i = bisect_right(a, x)
if i:
return a[i-1]
raise ValueError def find_gt(a, x):
'Find leftmost value greater than x'
i = bisect_right(a, x)
if i != len(a):
return a[i]
raise ValueError def find_ge(a, x):
'Find leftmost item greater than or equal to x'
i = bisect_left(a, x)
if i != len(a):
return a[i]
raise ValueError

以上内容摘自官方文档,官方文档有了中文版以后可太友好了。然后照猫画虎实现一个新的binary_search函数,参考上面的index()

def binary_search(nums: List[int], k: int) -> int:
if not nums:
raise ValueError('list is empty') i = bisect.bisect_left(nums, k)
if i != len(nums) and nums[i] == k:
return i
raise ValueError(f'{k} is not in list')

是不是比手动实现的二分查找简洁多了?

使用bisect库实现二分查找的更多相关文章

  1. python bisect 排序模块 二分查找与 bisect 模块

    python 3.6.5 import bisect bisect_list=dir(bisect)print(bisect_list)bisect_list = ['__builtins__', ' ...

  2. 二分查找与 bisect 模块

    Python 的列表(list)内部实现是一个数组,也就是一个线性表.在列表中查找元素可以使用 list.index() 方法,其时间复杂度为O(n).对于大数据量,则可以用二分查找进行优化.二分查找 ...

  3. python二分查找模块bisect

    bisect模块用于二分查找,非常方便. Bisect模块提供的函数有: 1.查找 bisect.bisect_left(a,x, lo=0, hi=len(a)) : 查找在有序列表a中插入x的in ...

  4. bisect 二分查找

    先说明的是,使用这个模块的函数前先确保操作的列表是已排序的. 先看看 insort  函数: 其插入的结果是不会影响原有的排序. 再看看 bisect  函数: 其目的在于查找该数值将会插入的位置并返 ...

  5. C++算法库学习__std::sort__对 vector进行排序_排序后就可以进行使用std::lower_bound进行二分查找(查找第一个大于等于指定值的迭代器的位置)__std::unique

    std::sort      对vector成员进行排序; std::sort(v.begin(),v.end(),compare);   std::lower_bound 在排序的vector中进行 ...

  6. 分治算法(二分查找)、STL函数库的应用第五弹——二分函数

    分治算法:二分查找!昨天刚说不写算法了,但是突然想起来没写过分治算法的博客,所以强迫症的我…… STL函数库第五弹——二分函数lower_bound().upper_bound().binary_se ...

  7. C++STL标准库学习笔记(二)二分查找

    二.STL中的二分查找算法 1.binary_search 2.lower_bound 3.upper_bound 记得#include<algorithm>! 前言: 在这个笔记中,我把 ...

  8. 【转】STL之二分查找 (Binary search in STL)

    Section I正确区分不同的查找算法count,find,binary_search,lower_bound,upper_bound,equal_range 本文是对Effective STL第4 ...

  9. 三分钟教你学Git(十三) - 二分查找

    比方说你收到了错误报告,然后你知道前几天明明是好的.可是这几天有好多新的commit被部署了.那么我们怎么迅速的找到第一个引入Bug的commit呢? 我们能够使用git bisect,git利用二分 ...

随机推荐

  1. 图解kubernetes调度器SchedulingQueue核心源码实现

    SchedulingQueue是kubernetes scheduler中负责进行等待调度pod存储的对,Scheduler通过SchedulingQueue来获取当前系统中等待调度的Pod,本文主要 ...

  2. 【Java基础总结】网络编程

    网络编程 InetAddress tcp udp

  3. PGSQL 日期时间的比较

    pgsql支持日期时间的比较,但是需要注意的是,我们写sql的时候传入的参数一般是字符串类型,我们需要把把字符串转化为Date类型,否则会查不到内容. 例子: select * from user w ...

  4. 数据结构与算法 Python语言实现 第一章练习

    说明:部分代码参考了Harrytsz的文章:https://blog.csdn.net/Harrytsz/article/details/86645857 巩固 R-1.1 编写一个Python函数 ...

  5. es5和es6中查找数组中的元素

    let array = [1,2,3,4,5] //es5 let find = array.filter(function (item){ return item %2 === 0//返回满足条件的 ...

  6. 读取Core下的appsettings.json的值的时候中文乱码

    这个百度一下一大堆,我就用的这个:然后重新生成一次就好了. 2.有的是更改VS的什么高级保存之类的,我记得之气设置过, 然后就是:这篇文章

  7. stormzhangB站直播之总结

    此文转自个人微信公众号,原链接为:https://mp.weixin.qq.com/s?__biz=MzUxODk0ODQ3Ng==&mid=2247484313&idx=1& ...

  8. 8.for循环及练习

    For循环:   虽然所有循环结构都可以用 while 或者 do...while 表示,但Java提供了另一种语句— —for循环,使一些循环结构变的更加简单. for 循环语句是支持迭代的一种通用 ...

  9. 18年第一弹射 和网络有关; 艾曲塞嗯诶系列篇 san

    62: 管理员想要更新 AR2200路由器的VRP, 正确的方法: 1)管理员把 AR2200 配置为FTP客户端,通过FTP来传输VRP软件 2)管理员把AR2200 配置为FTP服务器,通过FTP ...

  10. python 获取一个网页里的a 标签

    #!/usr/bin/python # -*- coding: utf-8 -*- #encoding=utf-8 #Filename:urllib2-header.py import re impo ...