中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如
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
提示:
-105 <= num <= 105
- 在调用
findMedian
之前,数据结构中至少有一个元素 - 最多
5 * 104
次调用addNum
和findMedian
大小顶堆法
中位数的题,一般都可以用大顶堆 + 小顶堆
来求解
- 小顶堆(minHeap):存储所有元素中较大的一半,堆顶存储的是其中最小的数。
- 大顶堆(maxHeap):存储所有元素中较小的一半,堆顶存储的是其中最大的数。
相当于把所有元素分成了大、小两半,这样计算中位数只需要大半部分的最小值和小半部分的最大值
class MedianFinder
{
//小顶堆存放较大的一半,堆顶元素是较大那一半中最小的
PriorityQueue<Integer> minHeap;
//大顶堆存放较小的一半,堆顶元素是较小那一半中最大的
PriorityQueue<Integer> maxHeap;
public MedianFinder()
{
minHeap = new PriorityQueue<>();
maxHeap = new PriorityQueue<>((a, b) -> b - a);
}
public void addNum(int num)
{
//一开始先插入小顶堆
if(minHeap.isEmpty() || num > minHeap.peek())
{
minHeap.offer(num);
//维持两个堆的平衡:小顶堆始终比大顶堆多[0,1]个元素
if (minHeap.size() - maxHeap.size() > 1)
maxHeap.offer(minHeap.poll());
}
else
{
maxHeap.offer(num);
//大顶堆的元素数量不超过小顶堆
if (maxHeap.size() - minHeap.size() > 0)
minHeap.offer(maxHeap.poll());
}
}
public double findMedian()
{
//中位数在小顶堆中
if(minHeap.size() > maxHeap.size())
return minHeap.peek();
//小顶堆和大顶堆的大小相同,中位数是两堆顶元素的中间值
else
return (minHeap.peek() + maxHeap.peek()) / 2.0;
}
}
时间复杂度:
- addNum: O(logn),其中 n 为累计添加的数的数量。
- findMedian: O(1)。
空间复杂度:
- O(n),主要为优先队列的开销。