heapq.nlargest和nsmallest是top-k问题最优解,时间复杂度o(n log k),仅维护大小为k的堆,内存占用o(k),支持key参数且不修改原数据;当k远小于n时性能显著优于sorted()。

直接用 heapq.nlargest 或 heapq.nsmallest,比先排序再切片快,尤其当 K 远小于数据总量时。
为什么不用 sorted()[:k]?
对 N 个元素排序时间复杂度是 O(N log N),而堆找 Top-K 只需 O(N log K) —— 当 K=10、N=10⁶ 时,log K ≈ 3.3,log N ≈ 20,性能差 6 倍以上。更关键的是,heapq.nlargest 内部用的是最小堆维护 K 个候选值,边遍历边淘汰,内存只占 O(K),不需加载全部数据到内存排序。
nlargest 和 nsmallest 的行为差异
两者都支持 key 参数,但注意:key 函数在每个元素上都会被调用一次,如果计算开销大(比如解析 JSON 字段),建议提前映射好;另外它们返回的是新列表,不修改原数据。
-
heapq.nlargest(3, data, key=lambda x: x['score'])返回 score 最高的 3 个字典 -
heapq.nsmallest(5, numbers)对纯数字列表最快,内部跳过 key 调用 - 若 K ≥ len(data),它们会退化为排序,此时不如直接用
sorted(data, key=...)
手动维护堆的适用场景
当需要流式处理(比如从文件逐行读、或实时接收数据)、且不能一次性把所有数据加载进内存时,就得自己用 heapq.heappushpop 或 heapq.heapreplace 维护固定大小的堆。
例如筛选前 100 个最大值:
import heapq <p>top_k = [] for item in stream_data: if len(top_k) top_k[0]: # top_k[0] 是当前最小值(最小堆) heapq.heapreplace(top_k, item) # 比 heappush + heappop 更快 </p>
注意:必须用最小堆存最大 K 个值(堆顶是最小的那个),反过来想就容易错;heapreplace 是原子操作,比先 push 再 pop 更安全高效。
真正容易被忽略的是 key 的副作用和堆的“方向感”——你存的是最大 K 个,堆却得是最小堆;你用 nsmallest 时传的却是“升序逻辑”,但底层还是靠堆结构保证效率。别被名字带偏,盯住你要留下的数据特征和堆顶代表什么。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











