尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python中的二分查找Bisect库使用实战
前言二分查找binary search是算法的入门课但 Python 标准库已经把它封装好了——bisect模块。它提供在已排序升序序列上做二分定位和插入的函数。很多人第一次用会踩同一个坑以为bisect返回的是「找到了没有」或者「元素的下标」。都不是。它返回的是一个「插入点」——如果要保持有序新元素应该插在这里。官方文档说得很直接这个模块的函数「设计用来定位插入点」因此它们从不调用__eq__来判断是否找到只用比较返回一个位于两个值之间的插入位置。理解这一点bisect的绝大多数困惑就解开了。本文专讲bisectbisect_left和bisect_rightbisect是后者的别名的区别、insort的插入、在有序序列上的查找以及「怎么用插入点反推出标准的查找结果」。示例以 Python 3.8 为基准key参数需要Python 3.10会单独标注。一、两个核心函数bisect_left与bisect_right签名分别是bisect_left(a, x, lo0, hilen(a), *, keyNone)和bisect_right(a, x, lo0, hilen(a), *, keyNone)。bisect是bisect_right的别名两者完全等价。差别在遇到相等的已有元素时插入点在左边还是右边bisect_left插在已有相等元素之前左。bisect_right插在已有相等元素之后右。# 适用于 Python 3.8import bisecta [10, 20, 20, 20, 30]print(bisect.bisect_left(a, 20)) # 1第一个 20 之前print(bisect.bisect_right(a, 20)) # 4最后一个 20 之后print(bisect.bisect(a, 20)) # 4bisect 就是 bisect_right对不存在的元素两者返回同一个值# 适用于 Python 3.8print(bisect.bisect_left(a, 25)) # 4print(bisect.bisect_right(a, 25)) # 4官方文档给出的不变式可以帮助记忆bisect_left返回的ip把数组切成两段左段满足所有元素 x右段满足所有元素 xbisect_right则是左段 x、右段 x。二、返回值的意义插入点不是命中这是全篇最该记住的一点。看下面这个容易误用的例子# 适用于 Python 3.8import bisecta [10, 20, 30]i bisect.bisect_left(a, 20)print(i) # 1print(a[i] 20) # True —— 这次碰巧找到了j bisect.bisect_left(a, 25)print(j) # 2print(a[j] 25) # False —— 25 根本不在表里bisect_left对 25 也返回了一个合法位置2但 25 并不存在。所以「返回值」本身不能作为「找到/没找到」的判断依据它只是「如果插入插哪里」。要把它变成真正的等值查找必须拿到位置后再自己比一次# 适用于 Python 3.8def index_of(a, x):i bisect.bisect_left(a, x)if i len(a) and a[i] x:return i # 找到了return -1 # 没找到print(index_of([10, 20, 30], 20)) # 1print(index_of([10, 20, 30], 25)) # -1注意i len(a)这个判断不能省如果x比所有元素都大i会等于len(a)此时a[i]直接越界。三、insort保持有序地插入insort_left和insort_rightinsort是后者的别名会先二分找位置再把元素插进去原地修改列表。# 适用于 Python 3.8import bisecta [10, 20, 30]bisect.insort(a, 25)print(a) # [10, 20, 25, 30]b [10, 20, 20]bisect.insort_left(b, 20)print(b) # [10, 20, 20, 20] —— 新元素在最左要清楚一个复杂度事实insort里二分是 O(log n)但插入本身要挪动后段元素是 O(n)整体被插入步骤主导。官方文档特别提醒了这一点——所以「频繁插入」的场景insort未必比别的方法划算尤其是在链表结构或堆更合适的时候。四、用插入点反推「最近的值」bisect天然适合回答「比 x 小的最大元素是哪个」「比 x 大的最小元素是哪个」这类范围查询。官方文档给的五个改造函数里可以提炼出最常用的两个# 适用于 Python 3.8import bisectdef find_le(a, x):返回 x 的最大元素i bisect.bisect_right(a, x)if i:return a[i - 1]raise ValueError(没有 x 的元素)def find_ge(a, x):返回 x 的最小元素i bisect.bisect_left(a, x)if i ! len(a):return a[i]raise ValueError(没有 x 的元素)a [10, 20, 30, 40]print(find_le(a, 25)) # 20print(find_ge(a, 25)) # 30find_le用的是bisect_right右插入点的前一个位置正好是「最后一个 x」的元素。find_ge用的是bisect_left左插入点本身正好是「第一个 x」的元素。另一个经典用法是分档查表比如按分数线给等级# 适用于 Python 3.8import bisectdef grade(score):breakpoints [60, 70, 80, 90]return FDCBA[bisect.bisect(breakpoints, score)]print([grade(s) for s in [59, 70, 90, 100]]) # [F, C, A, A]这里的FDCBA有 5 个字符5 个分数段bisect_right的返回值 0..4 正好当索引。五、key参数Python 3.10从Python 3.10起bisect系列函数新增了key参数可以在一张「记录表」上按某个字段定位。注意一个细节key函数只作用于序列里的元素不作用于被查找的x。# 适用于 Python 3.10import bisectmovies [Jaws, Titanic, Alien] # 假设已按 release 年份排序的另一组数据scores [(80, a), (85, b), (90, c)] # 已按分数升序# 查找第一个分数 88 的记录print(bisect.bisect_left(scores, 88, keylambda r: r[0])) # 2注意上面x传的是88这个裸数值因为它不会经过key而列表元素会被key抽成r[0]。如果写成keylambda r: r[1]、x又是元组就很容易对不上。常见坑点坑一把返回值当成「是否找到」。# 适用于 Python 3.8# ❌ 只要值在两元素之间就会返回一个位置import bisecta [10, 30]if bisect.bisect_left(a, 20):print(以为找到了 20) # 会打印但 20 并不在表里# ✅ 拿到位置后再比一次i bisect.bisect_left(a, 20)found i len(a) and a[i] 20 # False坑二在未排序的列表上用bisect。# 适用于 Python 3.8import bisect# ❌ bisect 假设序列已升序乱序结果是未定义的a [30, 10, 20]print(bisect.bisect_left(a, 20)) # 结果没有意义# ✅ 先排序a.sort()print(bisect.bisect_left(a, 20)) # 1坑三等值查找时忘了i len(a)。# 适用于 Python 3.8import bisecta [1, 2, 3]# ❌ x 比所有元素都大时i len(a)a[i] 越界i bisect.bisect_left(a, 9)print(a[i]) # IndexError: list index out of range# ✅ 先判边界再访问found i len(a) and a[i] 9 # False坑四混用左右得到不是想要的位置。# 适用于 Python 3.8import bisect# ❌ 想「插在最右边」却用了 lefta [20, 20]bisect.insort_left(a, 20) # [20, 20, 20]插在最左# ✅ 想插最右用 insort_right即 insorta [20, 20]bisect.insort_right(a, 20) # 插在最右坑五以为insort很快。# 适用于 Python 3.8import bisectdata [5, 3, 8, 1]# ❌ 以为 insort 整体是 O(log n)其实插入步骤要挪动后段元素是 O(n)keep_sorted []for x in data:bisect.insort(keep_sorted, x) # 每次插入都可能挪动后段# ✅ 只需要最终有序批量排一次更直接print(sorted(data)) # [1, 3, 5, 8]坑六key参数用在了 3.10 以下。# 适用于 Python 3.10import bisectrecords [(80, a), (90, b)]# ❌ 在 3.9 及更早版本里写 key... 会报 TypeError别照抄# bisect.bisect_left(records, 88, keylambda r: r[0])# ✅ 3.10 直接用 key 参数print(bisect.bisect_left(records, 88, keylambda r: r[0])) # 1# 旧版本替代写法先抽出键列表再对键列表二分keys [r[0] for r in records]print(bisect.bisect_left(keys, 88)) # 1坑七以为key也会作用到被查找的x。# 适用于 Python 3.10import bisectrecords [(80, a), (90, b)]# ❌ 让被查找的 x 也变成记录key 不作用于 x比较时类型不匹配print(bisect.bisect_left(records, (88, ?), keylambda r: r[0])) # TypeError# ✅ x 传「可比较的裸值」print(bisect.bisect_left(records, 88, keylambda r: r[0])) # 1总结函数返回等值时的位置bisect_left(a, x)插入点已有相等元素之前bisect_right(a, x)插入点已有相等元素之后bisect(a, x)插入点同bisect_rightinsort_left(a, x)None插入到最左insort_right(a, x)None插入到最右用bisect一定要记住两句话它返回插入点不返回「找到没找到」它假设序列已经升序。想拿它做等值查找就bisect_left得到位置后自己比一次想做范围查询就用左右两个插入点去取「相邻的那个元素」。把这两条理顺bisect就是一把非常顺手的工具。
RELATED

相关推荐

银河麒麟系统文件误删恢复实战与防护指南

银河麒麟系统文件误删恢复实战与防护指南

1. 银河麒麟系统文件误删的常见场景与痛点作为国产操作系统的代表,银河麒麟(Kylin)凭借其安全稳定的特性,在政务、金融、军工等领域广泛应用。但在日常使用中,文件误删问题困扰着不少用户。根据我的运维经验&#xff0…

📅 2026/10/28 12:32:45
GESP 2025年3月 原根判断 题解|数论详细讲解

GESP 2025年3月 原根判断 题解|数论详细讲解

记录一道 GESP 2025年3月 的数论真题。这类题在考试里属于必得分项,核心就那几行代码,关键是别在小细节上丢分。 题目来源:ACGO A62072 | 2025年3月 五级赛事场次:2025年3月 五级 一、题目信息 题目名称:原…

📅 2026/10/28 12:27:44
工业级爬虫数据存储优化与数据湖实践

工业级爬虫数据存储优化与数据湖实践

1. 工业级爬虫的挑战与数据入湖需求在数据驱动的商业环境中,爬虫技术早已从简单的数据抓取工具演变为企业数据基础设施的核心组件。传统爬虫脚本往往只关注"能否获取数据",而工业级爬虫系统需要解决的是"如何持续、可靠、高效地管理海量异…

📅 2026/10/28 12:27:44
MORE NEWS

更多资讯

📰

拆解 claude-mem:为 Claude Code 构建 AI 长期记忆层

你有没有遇到过这种场景:昨天刚和 AI 助手讨论完数据库选型,今天开个新会话想继续,结果它一脸茫然,连你用的是 PostgreSQL 都不记得,还要从头解释一遍。我在过去一周里被这个问题折磨得够呛,所以才认真把一…

📰

推挽电路从交越失真到直通:偏置、驱动与排障实战

做硬件这行,被问得最多的问题不是“这颗芯片怎么选”,而是“为什么我的输出一接上负载就塌了”。示波器上明明空载时波形干干净净,挂上几十欧的负载立刻变成一条被压扁的正弦,或者干脆只剩半个波。这类问题十有八九会指到同一个地…

📰

昇腾AI自定义算子开发实战:性能提升3倍的优化技巧

1. Ascend_C自定义算子开发全景解析在异构计算领域,算子开发能力直接决定了算法工程师能否充分发挥硬件性能。最近我在昇腾AI处理器上完成了一个图像超分项目的自定义算子开发,实测性能比通用算子提升了3倍以上。本文将完整分享从环境搭建到性能调优的全…

📰

Milvus not in 过滤性能爆炸:推荐系统已读去重架构改造复盘

先交代一下背景,我负责一个内容推荐服务的召回层,文档向量全量放在 Milvus 里,线上流量进来以后先做向量检索,再按用户状态做过滤。用户看完一篇内容就会被写进一个已读集合,下一次推荐时必须过滤掉。这个需求听起来人…

📰

昇腾Ascend_C自定义算子开发与性能优化指南

1. 为什么需要自定义算子开发在深度学习模型部署的实际场景中,我们经常会遇到框架原生算子库无法满足需求的情况。以华为昇腾(Ascend)平台为例,虽然其内置了丰富的算子库,但在以下场景中仍然需要开发者自行实现自定义算…

📰

WinForm中CSV转二进制序列化实战与性能优化

1. WinForm CSV转换与二进制序列化实战概述在工业控制、数据采集等传统Windows桌面应用场景中,WinForm依然是.NET开发者最常用的技术框架之一。最近在为一个设备监控系统开发数据导出功能时,我遇到了一个典型需求:将采集到的CSV格式数据高效转…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬