题目描述
// 41. 数据流中的中位数
// 力扣
// 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,
// 那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中
// 读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平
// 均值。
// 例如,
// [2,3,4] 的中位数是 3
// [2,3] 的中位数是 (2 + 3) / 2 = 2.5
// 设计一个支持以下两种操作的数据结构:
// void addNum(int num) - 从数据流中添加一个整数到数据结构中。
// double findMedian() - 返回目前所有元素的中位数。
// 牛客
// 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么
// 中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数
// 个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使
// 用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中
// 位数。
题解
/ 双堆维护 /
// 数据流要么有奇数个,要么有偶数个。
// 如果监控存入的元素个数size,必然size的变化有:奇数->偶数->奇数->偶数...
// 我们将第奇数个和第偶数个流入的数据分开,并使用最大堆最小堆来保存。
// 这是由于最大堆可以自动维护较小值(通过弹出最大值),最小堆可以自动维护较大值(通过弹出最小值。
// 由于第奇数个数先于第偶数个数存入,所以第奇数个数存入堆A(最大堆
// 还是最小堆无所谓),那么第偶数个数就必须存入堆B(是什么堆无所谓,反正跟A不同就行)
// 这个解里我令第1个,第3个...,这些第奇数个流入的数组存入最小堆,
// 令第2个,第4个...,第偶数个流入的数字存入最大堆。
// 数据在入堆之前还必须经过相反堆的筛选,比如第1个存入的数字规定存入最小堆
// 那么先进入最大堆进行筛选,此时最大堆弹出堆顶值(最大值),再存入最小堆,
// 第2个存入的数字同理,以此类推。
// 则最大堆会遍历数据流里排名奇数位的数字,并弹出的所有大值给最小堆
// 最小堆也会遍历数据流也排名偶数位的数字,并弹出所有的小值给最大堆。
// 经过这样的筛选,所有的大值都会给最小堆,所有的小值都会给最大堆。
// 求中位数时,只需看堆顶元素和数据流个数即可。
// 如果数据总数有偶数个,那么取大值里的最小值(最小堆堆顶)和
// 小值里的最大值(最大堆堆顶)求和÷2即可得到中位数。
// 如果数据总数为奇数个,取第奇数个存入堆(是最大堆还是最小堆无所谓)
// 的堆顶元素,就是中位数。
// 牛客
// 此解设第奇数个数字的存储堆为最小堆,第偶数个数据的存储堆为最大堆
// 运行时间:16ms
// 占用内存:10044k
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
// 最大堆maxHeap:用于筛选大值传入最小堆minHeap,自身用于保存小值。
private PriorityQueue<Integer> maxHeap = new PriorityQueue<>( new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
// 最小堆minHeap:用于筛选小值传入最大堆maxHeap,自身用于保存大值。
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
private int size = 0; // size让我们监控所有元素的总数
public void Insert(Integer num) {
// 如果已有偶数个元素,则将第奇数个元素num,存入最大堆中
// ,筛选出最大堆的最大值弹出,存入最小堆中。
// 如果当前数num在最大堆maxHeap就是最大值,会被直接存入
// 最小堆minHeap,如果在maxHeap中不是最大值,那么会maxHeap
// 会自己推出一个堆内当前的最大值给minHeap,相当于做了数据交换。
if (size % 2 == 0) {
maxHeap.add(num);
minHeap.add(maxHeap.poll());
}
else { // 如果已有奇数个元素,则将第偶数个元素存入最小堆中
minHeap.add(num);
// 后将最小堆中最小值弹出(筛选),存入最大堆中。
maxHeap.add(minHeap.poll());
}
size++; // 每存入一个数据,size+1,记录元素的总数
}
public Double GetMedian() {
// 两个堆元素相同,元素个数为偶数
if (maxHeap.size() == minHeap.size())
return (double) (maxHeap.peek() + minHeap.peek()) / 2.0;
else
return (double) minHeap.peek();
}
}
// 牛客
// 此解设第奇数个数字的存储堆为最大堆,第偶数个数据的存储堆为最小堆
// 运行时间 18ms
// 占用内存 10180KB
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
private PriorityQueue<Integer> maxHeap = new PriorityQueue<>( new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
private int size = 0;
public void Insert(Integer num) {
if (size % 2 == 0) {
minHeap.add(num);
maxHeap.add(minHeap.poll());
}
else {
maxHeap.add(num);
minHeap.add(maxHeap.poll());
}
size++;
}
public Double GetMedian() {
if (maxHeap.size() == minHeap.size())
return (double) (maxHeap.peek() + minHeap.peek()) / 2.0;
else
return (double) maxHeap.peek();
}
}
// 力扣
// 此解设第奇数个数字的存储堆为最小堆,第偶数个数据的存储堆为最大堆
// 执行用时:88 ms, 在所有 Java 提交中击败了34.84%的用户
// 内存消耗:49.4 MB, 在所有 Java 提交中击败了84.64%的用户
import java.util.PriorityQueue;
import java.util.Comparator;
class MedianFinder {
private PriorityQueue<Integer> maxHeap, minHeap;
int size;
public MedianFinder() {
maxHeap = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
minHeap = new PriorityQueue<>();
size = 0;
}
public void addNum(int num) {
if (size % 2 == 0) {
maxHeap.add(num);
minHeap.add(maxHeap.poll());
}
else {
minHeap.add(num);
maxHeap.add(minHeap.poll());
}
size++;
}
public double findMedian() {
if (minHeap.size() == maxHeap.size())
return (double) (minHeap.peek() + maxHeap.peek()) / 2.0;
else
return (double) minHeap.peek();
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
// 力扣
// 此解设第奇数个数字的存储堆为最大堆,第偶数个数据的存储堆为最小堆
// 执行用时:92 ms, 在所有 Java 提交中击败了26.29%的用户
// 内存消耗:49.7 MB, 在所有 Java 提交中击败了43.06%的用户
import java.util.PriorityQueue;
import java.util.Comparator;
class MedianFinder {
private PriorityQueue<Integer> maxHeap, minHeap;
int size;
public MedianFinder() {
maxHeap = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
minHeap = new PriorityQueue<>();
size = 0;
}
public void addNum(int num) {
if (size % 2 == 0) {
minHeap.add(num);
maxHeap.add(minHeap.poll());
}
else {
maxHeap.add(num);
minHeap.add(maxHeap.poll());
}
size++;
}
public double findMedian() {
if (minHeap.size() == maxHeap.size())
return (double) (minHeap.peek() + maxHeap.peek()) / 2.0;
else
return (double) maxHeap.peek();
}
}