直接用list.insert()不够快是因为查找和插入均为O(n),而bisect将查找优化至O(log n);insort_right在相等元素右侧插入,insort_left在左侧插入;所有bisect函数均要求列表严格升序,否则行为未定义。

为什么直接用 list.insert() 不够快?
有序列表插入新元素时,如果用 list.insert(i, x) 先找位置再插,查找本身就得 O(n),插入又触发后续元素平移,整体是 O(n)。而 bisect 把查找优化到 O(log n),虽然插入仍是 O(n)(底层还是数组),但对“查找开销敏感”的场景(比如频繁插入、列表较大)能明显减少 CPU 时间。
bisect.insort() 和 bisect.insort_left() 有什么区别?
两者都完成「二分查找 + 原地插入」,关键差异在相等元素的插入位置:
-
bisect.insort(a, x)等价于bisect.insort_right(a, x):把x插入到所有等于x的元素右侧 -
bisect.insort_left(a, x):把x插入到所有等于x的元素左侧
例如 a = [1, 2, 2, 3],插入 2 后:bisect.insort_right(a, 2) → [1, 2, 2, 2, 3](新 2 在末尾两个 2 之后)bisect.insort_left(a, 2) → [1, 2, 2, 2, 3](新 2 在开头两个 2 之前)——结果一样,但稳定性和索引语义不同。
插入前必须确保列表已排序,否则行为未定义
bisect 模块所有函数(包括 insort 系列)都假设输入列表严格升序。若列表无序,bisect.bisect_left() 可能返回错误索引,insort 会插到错位,破坏有序性,后续调用全失效。
立即学习“Python免费学习笔记(深入)”;
- 不要对刚
shuffle()或部分sort()的列表直接用insort - 若不确定是否有序,先用
all(a[i] 快速校验(仅调试用,生产环境避免此开销) - 更稳妥的做法:只在明确由你维护的、每次插入都走
insort的列表上使用
想获取插入位置而不实际插入?用 bisect.bisect_left() 配合手动 insert()
有时你需要位置索引做其他事(比如记录偏移、更新关联结构),而不是立即插入。这时别用 insort,改用查找函数:
import bisect a = [1, 3, 5, 7] pos = bisect.bisect_left(a, 4) # 返回 2 # 现在你可以:a.insert(pos, 4),或打印 pos,或检查 a[pos-1] 和 a[pos]
注意:bisect.bisect_left() 和 bisect.bisect_right() 返回的是「应插入的索引」,不是布尔值或元素本身;越界时(如插到末尾)返回 len(a),这是合法的 list.insert() 索引。
真正容易被忽略的是:bisect 不处理重复插入时的业务逻辑冲突。比如你用 insort_left 维护唯一键列表,但没额外去重,就会出现重复值——模块只管位置,不管语义。


















