双堆优于sorted list是因为插入和查询中位数均为o(log n),而sorted list为o(n);左堆用heapq存负值模拟最大堆,右堆用最小堆;需严格平衡两堆长度并注意符号翻转与空堆判断。

为什么不用 sorted list 而要双堆
因为每次插入后调用 sorted() 或维护 list 有序,平均时间复杂度是 O(n),而双堆(一个最大堆模拟左半、一个最小堆模拟右半)能把插入和中位数查询都压到 O(log n)。Python 没有内置最大堆,得用 heapq 存负值来模拟,这点容易漏掉符号翻转。
如何用 heapq 构建左右两个堆
左堆存较小的一半,用最大堆(实际存 -x);右堆存较大的一半,用最小堆(直接存 x)。关键约束是:len(right) == len(left) 或 len(right) == len(left) + 1,中位数就取右堆顶或左右堆顶均值。
实操建议:
- 初始化:用空
list建两个堆,left = [](存负值),right = [](存原值) - 插入逻辑:先 push 到
right,再把right[0]pop 出来 push 进left(取负),最后平衡大小——若len(left) > len(right),就把-heapq.heappop(left)推回right - 别忘了每次操作后调用
heapq.heapify()?不需要——所有heappush/heappop自动维护堆序
getMedian() 怎么写才不出错
中位数取决于总长度奇偶性,但更稳的方式是只看堆长关系:
- 如果
len(right) > len(left),返回right[0] - 如果相等,返回
(-left[0] + right[0]) / 2.0 - 永远不要假设
left非空——插入第一个数时left是空的,所以必须按上述条件分支,不能直接取left[0]
常见错误:忘记对 left[0] 取负,结果算出负中位数;或在插入时没做二次调整,导致两堆大小失衡,中位数偏移。
边界场景和性能注意点
双堆真正的坑不在主逻辑,而在初始化和极端输入:
- 插入
None或非数字会崩——加一层isinstance(x, (int, float))检查 - 大量重复数没问题,
heapq不要求唯一性 - 如果频繁查询中位数但插入少,其实用
SortedList(来自sortedcontainers)更直观;但题目明确要“快速查找中位数”且支持动态插入,双堆仍是标准解 - Python 的
heapq是 min-heap,想用 max-heap 必须手动取负——这个转换点在 push、pop、取顶三处都要同步,漏一处就全乱
最易被忽略的是:插入后没立刻 rebalance,比如先 push 到 left 再判断长度,会导致右堆空了还往里取 right[0] 报 IndexError。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











