bisect.insort() 比每次插入后 sort() 更高效,因其利用已有序前提,仅二分查找加线性插入,平均 o(n);而 sort() 每次重排全表,o(n log n)。

为什么直接用 bisect.insort() 比手动 sort() 更高效
每次插入后调用 list.sort() 时间复杂度是 O(n log n),而 bisect.insort() 利用已有序的前提,只做二分查找 + 线性插入,平均 O(n) —— 关键在“维持有序”这个动作本身不需要重排全部元素。
适用场景:频繁插入、偶尔读取、不需随机访问索引的队列式有序结构(比如优先级事件队列、滑动窗口中位数维护)。
-
bisect.insort_left()和bisect.insort_right()区别仅在相等元素插入位置:前者插在左侧(保持稳定顺序),后者插在右侧 - 如果列表初始无序,先
sorted()一次再用insort,否则行为未定义 - 不要对同一列表混用
append()+sort()和insort(),会破坏有序性假设
bisect.bisect() 和 bisect.bisect_left() 返回值到底代表什么
它们不修改列表,只返回插入位置索引。容易误解成“找到元素位置”,其实是在找“如果插入该值,应该放哪儿”。
例如 arr = [1, 3, 5, 7]:
import bisect bisect.bisect_left(arr, 5) # → 2(已有 5,插在它左边位置) bisect.bisect_right(arr, 5) # → 3(插在它右边位置) bisect.bisect(arr, 5) # 等价于 bisect_right
- 判断元素是否存在:用
arr[pos] == x and pos ,不能只靠返回值是否越界 - 查找上界/下界时,
bisect_left对应 lower_bound,bisect_right对应 upper_bound(C++ STL 语义) - 对空列表调用安全,返回 0
用 bisect 实现带去重的有序集合(类似 C++ set)
标准库没有内置有序去重容器,但可以用 bisect + 手动查重模拟。
关键不是避免重复插入,而是插入前确认不存在:
import bisect
<p>class SortedSet:
def <strong>init</strong>(self):
self._data = []</p><pre class="brush:python;toolbar:false;">def add(self, x):
i = bisect.bisect_left(self._data, x)
if i == len(self._data) or self._data[i] != x:
self._data.insert(i, x) # O(n) 插入,但比 sort 快
- 用
bisect_left查找后必须显式比较self._data[i],不能依赖i是否变化 - 删除操作也要配合
bisect定位,再用pop(i),仍是 O(n) - 如果增删极频繁且数据量大,考虑改用
sortedcontainers.SortedList(第三方,底层 C 优化)
常见报错:TypeError: '
这是 bisect 内部比较失败,不是你代码写错,而是列表里混了不可比较类型,或自定义类没实现 __lt__。
- 检查列表所有元素是否同类型,尤其注意
None、float('nan')、不同类实例混入 - 自定义类必须实现
__lt__(至少),推荐也实现__eq__方便查重 - 元组比较按字典序,
(1, 'a')和(1, 2)会报错,因为'a' 不合法
复杂点在于:错误发生在 bisect 内部深处,堆栈不显示你的调用行——定位方法是先用 all(isinstance(x, int) for x in lst) 类似检查,再逐段切片测试。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











