题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
分析:
- 解法一:可以使用数组来存放插入的数,然后进行排序,再根据数组长度为奇数偶数来确定中位数。注意:需要重写比较器接口。这种方法插入的时间复杂度为O(n),得到中位数的时间复杂度为O(1)。
import java.util.Comparator;
import java.util.ArrayList;
public class Solution {
private ArrayList<Integer> arrayList=new ArrayList<>();
public void Insert(Integer num) {
arrayList.add(num);
arrayList.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
}
public Double GetMedian() {
int size=arrayList.size();
if(size%2==0){
return new Double(arrayList.get(size/2)+arrayList.get((size/2)-1))/2;
}else{
return new Double(arrayList.get((size-1)/2));
}
}
}
- 解法二:使用大根堆小根堆来分别保存容器左边和右边的数,最大堆存的是到目前为止较小的那一半数,最小堆存的是到目前为止较大的那一半数,这样中位数只有可能是堆顶或者堆顶两个数的均值。使得保证容器左边的数据都小于容器右边的数据,这样即使左右两边内部的数据没有排序,也可以根据左边最大的数和右边最小的数来得到中位数。为了实现平均分配,可以在数据的总数目为偶数时,把新数据先插入大根堆得到最大的数在把它插入到小根堆,由于插入到小根堆的是原大根堆最大的数字,这样保证小根堆中所有数字都大于大根堆。当数据的总数目为奇数时,把新数据先插入小根堆得到最小的数然后在插入到大根堆,由于插入到大根堆的是原小根堆最小的数,这样也保证小根堆中所有数都小于大根堆。(也可以反过来,偶数的时候先插入小根堆,再插入大根堆,主要的目的就是为了保证两个堆中元素个数之差小于等于1)这种方法的插入的时间复杂度为O(logn),得到中位数的时间复杂度为O(1)。
package Solution51;
import java.util.Comparator;
import java.util.PriorityQueue;
//如何得到一个数据流中的中位数?
// 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
// 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
public class Solution {
private int count=0;
private PriorityQueue<Integer> minHeap=new PriorityQueue<>();
private PriorityQueue<Integer> maxHeap=new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
//左边区域为大根堆,右边区域为小根堆
public void Insert(Integer num) {
if(count%2==0){ //偶数时,加入小根堆(经大根堆筛选后的解进小根堆)
//把新数放入大根堆后,得到左边区域最大值后放入右边小根堆,确保右边的值都大于左边的值
maxHeap.offer(num);
int filteredMax=maxHeap.poll();
minHeap.offer(filteredMax);
}else { //奇数时,加入大根堆(经小根堆筛选后的解进小根堆)
minHeap.offer(num);
int filteredMin=minHeap.poll();
maxHeap.offer(filteredMin);
}
count++;
}
public Double GetMedian() {
if(count%2==0){
return new Double(minHeap.peek()+maxHeap.peek())/2;
}else{
return new Double(minHeap.peek());
}
}
}