数据流的中位数
中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如 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
解题思路
- 1、使用两个优先队列(PriorityQueue),一个最大堆用于存储数据流的前半部分,一个最小堆用于存储数据流的后半部分。
- 2、维护两个堆,使得最大堆的大小等于或比最小堆的大小大1,这样中位数就可以直接从堆顶元素中获取。
- 3、当新的元素加入数据流时,根据元素的大小,将其插入到最大堆或最小堆中,并调整两个堆,使得满足上述条件。
Java实现
java
private PriorityQueue<Integer> maxHeap; // 存储较小一半的元素
private PriorityQueue<Integer> minHeap; // 存储较大一半的元素
public MedianFinder() {
maxHeap = new PriorityQueue<>(Collections.reverseOrder());
minHeap = new PriorityQueue<>();
}
public void addNum(int num) {
if (maxHeap.isEmpty() || num <= maxHeap.peek()) {
maxHeap.offer(num);
} else {
minHeap.offer(num);
}
// 平衡两个堆,使大堆的size == 小堆的size 或者 小堆的size+1
if (maxHeap.size() > minHeap.size() + 1) {
minHeap.offer(maxHeap.poll());
} else if (minHeap.size() > maxHeap.size()) {
maxHeap.offer(minHeap.poll());
}
}
public double findMedian() {
if (maxHeap.isEmpty() && minHeap.isEmpty()) {
return 0;
}
if (maxHeap.size() == minHeap.size()) {
return (maxHeap.peek() + minHeap.peek()) / 2.0;
} else {
return maxHeap.peek();
}
}
时间空间复杂度
- 时间复杂度:
addNum方法的时间复杂度为O(log n),其中n为数据流中元素的个数,因为在插入元素时需要维护堆的平衡。
findMedian方法的时间复杂度为O(1),因为只需要获取堆顶元素即可。
- 空间复杂度:
由于使用了两个优先队列,所以空间复杂度为O(n)。