手动实现

假如有一个有序表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. Scala实践4

    一.数组 在Scala中,用()来访问元素,数组声明的语法格式如下 : var z:Array[String] = new Array[String](3) 或 var z = new Array[S ...

  2. Numpy的介绍与基本使用方法

    1.什么是Numpy numpy官方文档:https://docs.scipy.org/doc/numpy/reference/?v=20190307135750 NumPy是一个功能强大的Pytho ...

  3. Django框架初体验

    前言 从今天开始学习测试开发知识,并会把每一次学习的过程和成果记录到博客,由于我也没怎么接触过python相关的开发框架,所以前期应该是艰难的,但是我相信努力就会有收获,如果你和我一样是个小白,那我们 ...

  4. C#中的委托是什么

    1.什么是委托?(方法作另一个方法的参数)delegate void MyDel(int value);    //声明委托类型和类一样,委托是用户自定义的类型,但是类是数据和方法的集合,而委托是持有 ...

  5. Mysql.新建数据库和用户

    //建立数据库 drop database if exists 你的db名; create database 你的db名 CHARACTER SET utf8 COLLATE utf8_general ...

  6. java8 stream自定义分组求和并排序

    public static void main(String[] args) { List<GroupDetailDTO> list = new ArrayList<>(); ...

  7. [bzoj1070] [洛谷P2053] [SCOI2007] 修车

    Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同 的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序, ...

  8. 「 从0到1学习微服务SpringCloud 」02 Eureka服务注册与发现

    系列文章(更新ing): 「 从0到1学习微服务SpringCloud 」01 一起来学呀! Spring Cloud Eureka 基于Netflix Eureka做了二次封装(Spring Clo ...

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

    1:当指定接口运行在RIP2组播方式时,以下说法正确的是 2个答案 A 只接收RIPv2组播报文 B  不接收RIPV1 广播报文 2 下面哪条命令是把PPP的认证方式设置为PAP? C ppp au ...

  10. HttpApplication IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable ps url System.Web.dll

    // 摘要:     //     定义 ASP.NET 应用程序中的所有应用程序对象共有的方法.属性和事件.此类是用户在 Global.asax 文件中所定义的应用程序的基类.     [Toolb ...