Python heapq库案例详解
前言heapq是 Python 标准库里实现堆heap的模块用它可以很省事地做出优先队列。它的核心事实只有一条默认是最小堆——堆顶heap[0]永远是最小的元素弹出也按从小到大。很多人的第一反应是「我要最大堆怎么办」然后去翻文档想找一个max_heap。在Python 3.14 之前标准库里没有最大堆函数惯用做法是把数值取负后再当最小堆用。3.14 起官方补上了_max后缀的一整套函数这个版本界限要分清楚。本文把heapq的常用函数逐个讲清heapify、heappush、heappop、heappushpop、heapreplace以及nlargest/nsmallest和merge再讲两条实战套路——用取负实现最大堆以及用(优先级, 序号, 任务)三元组避免不可比较的项互相比较。示例以 Python 3.8 为基准3.14 的新函数会标注版本。一、最小堆的三个基本操作heapq不提供堆类型而是把普通列表当作堆用一组函数去操作它。这带来一个好处heap[0]就是最小值直接下标即可读取。# 适用于 Python 3.8import heapqh [] # 空列表就是一个空堆heapq.heappush(h, 5)heapq.heappush(h, 1)heapq.heappush(h, 3)print(h[0]) # 1堆顶最小值只读取不弹出print(heapq.heappop(h)) # 1弹出并返回最小值print(heapq.heappop(h)) # 3heappop在空堆上会抛IndexError。想「看一眼」最小值而不弹出就用h[0]。heapify把已有的列表原地整理成堆它不开新列表# 适用于 Python 3.8nums [5, 1, 3, 2, 4]heapq.heapify(nums) # 原地返回 Noneprint(nums[0]) # 1注意heapify之后列表内部的顺序看起来「乱了」这是堆的数组表示不是排序结果要得到有序结果得反复heappop。二、heappushpop和heapreplace这两个是「一推一弹」的合并操作适合固定大小的堆。# 适用于 Python 3.8import heapqh [1, 3, 5]heapq.heapify(h)# heappushpop先推入再弹出最小值返回弹出的那个print(heapq.heappushpop(h, 0)) # 0推入 0 后弹出的最小值就是 0print(heapq.heappushpop(h, 4)) # 1推入 4弹出的是原来的最小值 1print(sorted(h)) # [3, 4, 5]# heapreplace先弹出最小值再推入新值堆大小不变h2 [1, 3, 5]heapq.heapify(h2)print(heapq.heapreplace(h2, 9)) # 1先弹出 1再放入 9print(sorted(h2)) # [3, 5, 9]两者最容易混的一点heappushpop返回的是「推入值和原最小值里较小的那个」因为它先推后弹heapreplace返回的是被弹掉的原最小值返回值可能比新放入的值大。官方文档明确提醒如果你不希望返回值比新值还大用heappushpop。heappushpop和heapreplace在空堆上的行为也不同heapreplace空堆会抛IndexErrorheappushpop空堆时等价于直接推入并返回它。三、nlargest、nsmallest和merge不想自己建堆时这三个是「开箱即用」的工具。# 适用于 Python 3.8import heapqnums [3, 1, 4, 1, 5, 9, 2, 6]print(heapq.nlargest(3, nums)) # [9, 6, 5]print(heapq.nsmallest(3, nums)) # [1, 1, 2]data [(b, 2), (a, 3), (c, 1)]print(heapq.nlargest(2, data, keylambda x: x[1])) # [(a, 3), (b, 2)]nlargest(n, iterable, keyNone)等价于sorted(iterable, keykey, reverseTrue)[:n]nsmallest类似。官方提示n较小时它们更划算n很大时直接用sorted更好n 1时min/max更直接。merge把多个已排序的输入合并成一个有序迭代器不需要一次性把所有数据读进内存# 适用于 Python 3.8import heapqprint(list(heapq.merge([1, 3, 5], [2, 4, 6]))) # [1, 2, 3, 4, 5, 6]四、最大堆取负或者用 3.14 的_max前面说过heapq默认是最小堆。要在旧版本上得到「每次弹出最大值」的效果最常用的技巧是把值取负存进去弹出时再取负还原。# 适用于 Python 3.8import heapqh []for x in [3, 1, 4, 1, 5]:heapq.heappush(h, -x) # 存负数print(-h[0]) # 5取负还原出最大值print(-heapq.heappop(h)) # 5print(-heapq.heappop(h)) # 4从Python 3.14 起标准库直接提供了最大堆函数heapify_max、heappush_max、heappop_max、heappushpop_max、heapreplace_max。它们和最小堆版本一一对应只是维持的是「父节点不小于子节点」的相反不变式。# 适用于 Python 3.14import heapqh []heapq.heappush_max(h, 3)heapq.heappush_max(h, 1)heapq.heappush_max(h, 5)print(h[0]) # 5最大堆的堆顶是最大值print(heapq.heappop_max(h)) # 5如果你需要在 3.14 以前的版本上跑就用取负那套不要指望旧版本有_max函数。五、优先队列(优先级, 序号, 任务)三元组把任务放进堆通常会存成(优先级, 任务)。但如果两个任务优先级相同元组比较会去比第二个元素——一旦任务本身不可比较就报TypeError。解决办法是插入一个序号当第二项(优先级, 序号, 任务)。序号唯一所以比较永远停在第二项不会去比任务。# 适用于 Python 3.8import heapqimport itertoolscounter itertools.count() # 递增计数器保证序号唯一pq []def add_task(priority, name):heapq.heappush(pq, (priority, next(counter), name))add_task(2, 写文档)add_task(1, 修 bug)add_task(2, 开会)while pq:priority, _, name heapq.heappop(pq)print(priority, name)# 1 修 bug# 2 写文档# 2 开会输出里两个优先级为 2 的任务按加入顺序返回这正是序号这个 tie-breaker决胜项带来的稳定性。官方文档给的优先队列方案就是这个三元组结构。常见坑点坑一把heapq当成能直接取最大值。# 适用于 Python 3.8# ❌ 默认是最小堆h[0] 是最小值import heapqh [3, 1, 2]heapq.heapify(h)print(h[0]) # 1# ✅ 要最大值就取负print(-min(-x for x in h)) # 3坑二heapify后以为列表已排序。# 适用于 Python 3.8import heapq# ❌ 堆不是有序列表nums [5, 1, 3, 2, 4]heapq.heapify(nums)print(nums) # 只是满足堆性质不是 [1,2,3,4,5]# ✅ 要排序就反复 heappop或直接 sortedprint(sorted(nums))坑三直接比较的任务对象放进(优先级, 任务)。# 适用于 Python 3.8import heapq# ❌ 优先级相同又比不出任务大小会 TypeErrorpq []heapq.heappush(pq, (1, {name: a})) # dict 不可比较heapq.heappush(pq, (1, {name: b})) # 触发比较 → TypeError# ✅ 用 (优先级, 序号, 任务)import itertoolscounter itertools.count()heapq.heappush(pq, (1, next(counter), {name: a}))heapq.heappush(pq, (1, next(counter), {name: b}))坑四heappop空堆。# 适用于 Python 3.8# ❌ 空堆弹出抛 IndexErrorimport heapqheapq.heappop([]) # IndexError: index out of range# ✅ 先判空h []if h:heapq.heappop(h)坑五heappushpop和heapreplace的返回值搞反。# 适用于 Python 3.8import heapq# ❌ 以为 heapreplace 返回的是新推入的值h [1, 5]heapq.heapify(h)print(heapq.heapreplace(h, 9)) # 1返回的是被弹掉的最小值# ✅ 用 heappushpop 才返回「两者中较小的」h [1, 5]print(heapq.heappushpop(h, 9)) # 1坑六在 3.13 及更早调用heappush_max。# 适用于 Python 3.8heappush_max 需 3.14import heapq# ❌ 在 3.13 及更早版本调用 heappush_max 会报 AttributeError# heapq.heappush_max(h, 5)# ✅ 旧版本把值取负用最小堆模拟最大堆h []heapq.heappush(h, -5)print(-h[0]) # 5坑七nlargest的n传得比数据还大。# 适用于 Python 3.8import heapq# ❌ 容易忽略返回值长度会被截断print(heapq.nlargest(10, [1, 2])) # [2, 1]只有 2 个# ✅ n 较大或接近全长时直接排序更省心print(sorted([1, 2], reverseTrue))总结函数作用备注heapify(x)原地变成最小堆返回Noneheappush(h, item)入堆维护堆性质heappop(h)弹出最小值空堆抛IndexErrorheappushpop(h, item)先推后弹返回两者较小值heapreplace(h, item)先弹后推返回被弹的最小值nlargest/nsmallest取最大/最小 n 个支持keyheapify_max等最大堆3.14旧版本用取负heapq的用法可以浓缩成三句话它是操作列表的最小堆堆顶是h[0]要最大堆旧版本取负、3.14 起用_max函数要放不可比较的对象就在元组里插一个递增序号。把这三条吃透优先队列、Top-K、定时任务调度这类需求都能用几行代码写出来。

相关新闻

Kubernetes离线部署MySQL 5.7全量包设计与金融级交付实践

Kubernetes离线部署MySQL 5.7全量包设计与金融级交付实践

简介:本资源是一套面向Kubernetes初学者与运维工程师的MySQL 5.7全量离线部署方案,专为无外网环境或需快速落地生产级数据库服务的场景设计,解决容器化MySQL部署中镜像拉取失败、YAML配置繁琐、数据初始化困难等典型问题。压缩包共7个文件&am…

2026/11/5 10:04:48 阅读更多 →
Linux C进程管理实战:多进程协作、生命周期与调试要点

Linux C进程管理实战:多进程协作、生命周期与调试要点

做Linux下的C开发,进程管理这块我前前后后折腾了不少年头。之前写过一篇基础篇,主要讲进程的概念和基本API,这次趁03.15这个节点把第二篇整理出来。这篇重点放在"管理"两个字上,不是单纯列接口文档,而是把我…

2026/10/27 12:42:04 阅读更多 →
hyperframes框架实战:IMU/GPS数据到高精度轨迹预测的完整方案

hyperframes框架实战:IMU/GPS数据到高精度轨迹预测的完整方案

从“静默的导航数据”到“可用的位置轨迹”:一次引入 hyperframes 框架的完整实录如果你手上正好握着一大堆 IMU 或 GPS 传感器数据,想从里面还原一个人的真实轨迹、判断他是在走路还是拐弯、甚至预测他接下来几秒会出现在哪里,那hyperframes…

2026/11/1 10:30:41 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/5 12:28:00 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/5 1:08:50 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/5 12:28:01 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/5 12:28:02 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/5 0:54:28 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/5 19:38:59 阅读更多 →

日新闻

周新闻

月新闻