结论:bisect模块查找比手写二分或list.index()快得多,因前者o(log n)最多20次比较,后者o(n)平均50万次;但要求列表严格有序且只查不改,否则需自行维护有序性。

直接说结论:用 bisect 模块做查找,比手写二分或用 list.index() 快得多,但前提是列表必须严格有序,且你只查不改——一旦插入/删除,就得自己维护有序性。
为什么不能直接用 list.index() 查找?
在长度为 10⁶ 的有序列表里找一个值:list.index() 是 O(n) 线性扫描,平均要比较 50 万次;bisect.bisect_left() 是 O(log n),最多比较 20 次。实测差距常在 100 倍以上。
- 常见错误现象:
ValueError: xxx is not in list—— 这不是bisect报的,是手写循环里没找到还硬 return 导致的;bisect从不抛这个错,它只返回位置索引 - 使用场景:日志时间戳排序后查某秒内的第一条记录、股票价格序列查首次突破阈值点、IP 地址段匹配
- 注意:
bisect不检查输入是否有序,传入乱序列表会返回错误位置,且无任何警告
bisect_left 和 bisect_right 到底怎么选?
关键看你要处理重复元素时的行为。假设列表是 [1, 2, 2, 2, 3],查 2:
-
bisect_left(a, 2)返回1(最左插入点,即第一个2的位置) -
bisect_right(a, 2)返回4(最右插入点,即最后一个2后面的位置) - 所以判断是否存在:
if a[bisect_left(a, x)] == x:—— 但得先确保索引不越界:pos = bisect_left(a, x); if pos - 查范围(如所有等于 x 的元素):
left = bisect_left(a, x); right = bisect_right(a, x); a[left:right]
插入新元素时如何保持有序?
别先 append 再 sort —— 那是 O(n log n),完全毁掉优势。用 insort_left 或 insort_right:
- 它们内部调用对应
bisect_*找位置,再用list.insert()插入,整体 O(n),但比重排快得多 - 性能影响:单次插入仍是 O(n),因为 Python 列表底层是数组,插入中间要搬移元素;如果插入非常频繁,该换
sortedcontainers.SortedList或自己维护平衡树 - 兼容性注意:Python bisect.insort 不支持
key参数;想按字段排序插入,得先确保列表按该字段建好,不能临时传 key
真正容易被忽略的是:bisect 只解决“查”和“插”的局部效率,但如果你一边查一边大量删,或者需要动态范围聚合(比如区间和),那光靠它不够——得上 array + 手动二分,或者直接切到 NumPy 的 searchsorted,甚至考虑用 B-tree 类库。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











