题目
中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如
arr = [2,3,4]
的中位数是3
。 - 例如
arr = [2,3]
的中位数是(2 + 3) / 2 = 2.5
。
实现 MedianFinder 类:
-
MedianFinder()
初始化MedianFinder
对象。 -
void addNum(int num)
将数据流中的整数num
添加到数据结构中。 -
double findMedian()
返回到目前为止所有元素的中位数。与实际答案相差10-5
以内的答案将被接受。
示例
示例 1:
输入 ["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"] [[], [1], [2], [], [3], []] 输出 [null, null, null, 1.5, null, 2.0] 解释 MedianFinder medianFinder = new MedianFinder(); medianFinder.addNum(1); // arr = [1] medianFinder.addNum(2); // arr = [1, 2] medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2) medianFinder.addNum(3); // arr[1, 2, 3] medianFinder.findMedian(); // return 2.0
分析
为了高效地维护数据流中的中位数,可以使用两个堆(最大堆和最小堆)来分别保存较小的一半元素和较大的一半元素。最大堆的堆顶是较小部分的最大值,最小堆的堆顶是较大部分的最小值,这样可以在 O () 的时间内完成插入和中位数查询操作。
代码解释
数据结构:
maxHeap
:使用最大堆保存较小的一半元素,堆顶是这部分的最大值。
minHeap
:使用最小堆保存较大的一半元素,堆顶是这部分的最小值。
添加元素(addNum
):
根据当前元素与maxHeap
堆顶的大小关系,决定将元素插入到maxHeap
(较小部分)还是minHeap
(较大部分)。
插入后调整两个堆的大小,确保maxHeap
的大小要么等于minHeap
,要么比其大 1,以维持平衡。
查找中位数(findMedian
):
如果总元素个数为奇数,中位数是maxHeap
的堆顶(因为maxHeap
的大小比minHeap
大 1)。
如果总元素个数为偶数,中位数是两个堆堆顶元素的平均值。
时间复杂度:addNum—
O(),
是当前元素个数,
findMedian—
O(1)
空间复杂度:O()
class MedianFinder {
private:
std::priority_queue<int> maxHeap; // 保存较小的一半元素,最大堆
std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; // 保存较大的一半元素,最小堆
public:
MedianFinder() {}
void addNum(int num) {
// 根据数值大小选择插入的堆
if (maxHeap.empty() || num <= maxHeap.top()) {
maxHeap.push(num);
} else {
minHeap.push(num);
}
// 调整堆的大小,保持maxHeap的大小为minHeap的大小或比其大1
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()) % 2 == 1) {
// 奇数个元素,中位数是maxHeap的堆顶(因为maxHeap大小多1)
return static_cast<double>(maxHeap.top());
} else {
// 偶数个元素,中位数是两个堆顶的平均值
return (static_cast<double>(maxHeap.top()) + minHeap.top()) / 2.0;
}
}
};