对顶堆

对顶堆(Double-Heap)

简介

对顶堆是一种用于动态维护数据流中位数的常用技巧。它利用一个大根堆和一个小根堆,在数据不断插入时高效地获取当前中位数(或其他分位数)。

核心思想

  • 大根堆(max-heap):存储数据中较小的一半,堆顶为这部分的最大值。
  • 小根堆(min-heap):存储数据中较大的一半,堆顶为这部分的最小值。

保持两堆元素数量平衡:

  • 总元素个数为奇数时,大根堆多存一个元素,其中位数就是大根堆堆顶。
  • 总元素个数为偶数时,两堆元素数相等,中位数为(大根堆堆顶 + 小根堆堆顶)/ 2。

操作步骤

插入元素 x

  1. 若大根堆为空或 x ≤ 大根堆堆顶,则插入大根堆;否则插入小根堆。
  2. 平衡两堆大小:
    • 大根堆.size() > 小根堆.size() + 1,将大根堆堆顶移至小根堆。
    • 小根堆.size() > 大根堆.size(),将小根堆堆顶移至大根堆。

查询中位数

  • 若两堆大小相等:(大根堆堆顶 + 小根堆堆顶) / 2.0
  • 若大根堆多一个元素:大根堆堆顶

复杂度

  • 插入:O(log n)
  • 查询中位数:O(1)

应用场景

  • 数据流中的中位数(LeetCode 295)
  • 动态统计中的实时分位数
  • 滑动窗口中的中位数(配合延迟删除)

代码示例

C++

#include <queue>
#include <vector>
using namespace std;

class MedianFinder {
private:
    priority_queue<int> maxHeap; // 大根堆,存较小的一半
    priority_queue<int, vector<int>, greater<int>> minHeap; // 小根堆,存较大的一半

public:
    void addNum(int num) {
        if (maxHeap.empty() || num <= maxHeap.top()) {
            maxHeap.push(num);
        } else {
            minHeap.push(num);
        }

        // 平衡两堆大小
        if (maxHeap.size() > minHeap.size() + 1) {
            minHeap.push(maxHeap.top());
            maxHeap.pop();
        } else if (minHeap.size() > maxHeap.size()) {
            maxHeap.push(minHeap.top());
            minHeap.pop();
        }
    }

    double findMedian() {
        if (maxHeap.size() == minHeap.size()) {
            return (maxHeap.top() + minHeap.top()) / 2.0;
        } else {
            return maxHeap.top();
        }
    }
};
posted @ 2026-06-03 21:42  十七code  阅读(11)  评论(0)    收藏  举报