不能直接用PriorityQueue求中位数,因其仅支持O(1)获取堆顶,无法高效取得第⌊n/2⌋小元素;须用大顶堆存较小一半、小顶堆存较大一半,并始终维持大小差≤1且堆顶有序。

为什么不能直接用 PriorityQueue 求中位数?
因为 PriorityQueue 只能高效获取堆顶(最小或最大),无法随机访问中间元素,更不能在 O(1) 或 O(log n) 时间内拿到第 ⌊n/2⌋ 小的值。中位数要求动态维护“中间位置”,必须靠两个堆协同——一个大顶堆存较小一半,一个小顶堆存较大一半。
怎么初始化大小顶堆才能保证平衡?
Java 没有原生大顶堆,得用 PriorityQueue 配合 Comparator.reverseOrder();小顶堆用默认构造。关键不是初始容量,而是始终维持:maxHeap.size() == minHeap.size() 或 maxHeap.size() == minHeap.size() + 1。
实操建议:
- 始终让
maxHeap(大顶堆)保存较小的一半,所以它的堆顶就是当前中位数的“左候选” - 每次
addNum(int num)后,先塞进maxHeap,再把它的堆顶弹出塞进minHeap,最后检查并调整两堆大小关系 - 避免直接比较
num和堆顶再决定插入哪个堆——容易漏掉边界情况(比如堆为空时)
addNum 过程中常见的堆失衡错误
典型现象:中位数突然跳变、偶数个数时返回错值、NullPointerException(空堆调 peek())。
立即学习“Java免费学习笔记(深入)”;
根本原因往往是没统一处理“先入 maxHeap → 调整 → 再平衡”的流程,而是写成条件分支判断。正确做法是固定流水线:
- 把新数加进
maxHeap - 立刻弹出
maxHeap.peek(),放进minHeap - 如果
minHeap.size() > maxHeap.size(),就把minHeap.poll()塞回maxHeap - 所有
peek()前加非空判断(尤其首次添加时)
示例片段:
if (!maxHeap.isEmpty() && num > maxHeap.peek()) { ... } 是错的——这会破坏双堆不变量,应全程走统一调度逻辑。
取中位数时 findMedian 的性能和精度陷阱
看似简单,但两个细节极易出错:一是整数除法导致精度丢失(如 (a + b) / 2 在 a,b 都是 int 时截断),二是堆大小判断逻辑写反。
实操要点:
- 返回类型必须是
double,计算时至少一个操作数转double:(double) maxHeap.peek() / 1.0或(maxHeap.peek() + minHeap.peek()) / 2.0 - 只在
maxHeap.size() == minHeap.size()时取两堆顶平均;否则直接返回maxHeap.peek()(因约定它多一个) - 别依赖
size()的绝对值做分支,而要用相对关系——比如maxHeap.size() - minHeap.size() == 1比maxHeap.size() == 1更鲁棒
双堆模型真正难的不是代码长度,而是每一步都得守住“maxHeap 顶 ≤ minHeap 顶”且“大小差 ≤ 1”这两个不变量。漏一次校验,后续全乱。









