描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
示例1
输入:
[5,2,3,4,1,6,7,0,8]
返回值:
"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明:
数据流里面不断吐出的是5,2,3…,则得到的平均数分别为5,(5+2)/2,3…
题解
方法一:插入排序法
利用一个list记录不断插入的数字,如果插入后的数字是排序的,那么直接通过下标即可找到中位数所在。这里就采用了插入排序的思想:将新元素插入到一个已排序的序列中,并使得新序列仍有序。实现方法从后向前(从前向后也可)比较,遇到比新元素大(小)的,就交换当前元素和新元素。
java实现如下:
import java.util.ArrayList;
public class Solution {
//1.插入排序法
ArrayList<Integer> cap = new ArrayList<>();
public void Insert(Integer num) {
cap.add(num);
for(int i = cap.size() - 1; i >= 1; i --){
if(cap.get(i) < cap.get(i - 1)){
int temp = cap.get(i);
cap.set(i, cap.get(i - 1));
cap.set(i - 1, temp);
}
}
}
public Double GetMedian() {
if(cap.size() % 2 == 1) return (double)cap.get((cap.size() / 2));
return (double)((cap.get(cap.size() / 2) + cap.get(cap.size() / 2 - 1)) / 2.0);
}
}
方法二:大顶堆、小顶堆记录中位数
可以分别设置一个大顶堆和一个小顶堆,然后交替向大顶堆、小顶堆插入新元素,通过维护两个堆顶,即可随时取出中位数。比如,如果先向大顶堆插入的话,如果插入的数量为奇数,那么maxHeap.peek()就是中位数;如果插入数量为偶数,那么(minHeap.peek() + maxHeap.peek()) / 2就是中位数。
那么该如何维护两个堆呢?这里就用到了中位数的概念,我们可以让小顶堆中任意元素都比大顶堆中任意元素大,即minHeap.peek() > maxHeap.peek(),那么minHeap.peek()和maxHeap.peek()一定是序列最中间的两个数。
代码实现如下:
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
// 1.插入排序法
// ArrayList<Integer> cap = new ArrayList<>();
// public void Insert(Integer num) {
// cap.add(num);
// for(int i = cap.size() - 1; i >= 1; i --){
// if(cap.get(i) < cap.get(i - 1)){
// int temp = cap.get(i);
// cap.set(i, cap.get(i - 1));
// cap.set(i - 1, temp);
// }
// }
// }
// public Double GetMedian() {
// if(cap.size() % 2 == 1) return (double)cap.get((cap.size() / 2));
// return (double)((cap.get(cap.size() / 2) + cap.get(cap.size() / 2 - 1)) / 2.0);
// }
//2.利用大顶堆和小顶堆,让小顶堆所有元素比大顶堆大
PriorityQueue<Integer> minHeap = new PriorityQueue<>(11); //默认为小顶堆
//大顶堆可以用比较器实现
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(11, new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return o2 - o1;
}
});
int index = 0;
public void Insert(Integer num) {
if(index % 2 == 0){
if(!minHeap.isEmpty() && num > minHeap.peek()){
maxHeap.offer(minHeap.poll());
minHeap.offer(num);
}else{
maxHeap.offer(num);
}
}else{
if(!maxHeap.isEmpty() && num < maxHeap.peek()){
minHeap.offer(maxHeap.poll());
maxHeap.offer(num);
}else{
minHeap.offer(num);
}
}
index ++;
}
public Double GetMedian() {
if(index == 0) return 0.0;
if(index % 2 == 1) return (double)maxHeap.peek();
//注意返回值为double
return (maxHeap.peek() + minHeap.peek()) / 2.0;
}
}