package com.cyy.test_t_6_;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
/**
* @Description 剑指Offer t_63- 数据流中的中位数
* 题目描述
* 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
* 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
* 我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
*
* 思路分析:
* 用两个堆保存数据,保证两个堆的数据保持平衡(元素个数相差不超过1)大顶堆存放的数据要比小顶堆的数据小,
*
* 大顶堆存放的数据要比小顶堆的数据小当两个堆中元素为偶数个,将新加入元素加入到大顶堆,
* 如果要加入的数据,比小顶堆的最小元素大,先将该元素插入小顶堆,然后将小顶堆的最小元素插入到大顶堆;
*
* 当两个堆中元素为奇数个,将新加入元素加入到小顶堆,
* 如果要加入的数据,比大顶堆的最大元素小,先将该元素插入大顶堆,然后将大顶堆的最大元素插入到小顶堆。
*
* @Author Crystal
* @Date 2019/7/23 14:56
* @Version 1.0
**/
public class t_63 {
//用来记录两个堆中的数据量
private static int count;
//小顶堆
private static PriorityQueue<Integer> minHeap = new PriorityQueue<>();
//大顶堆
private static PriorityQueue<Integer> maxHeap = new PriorityQueue<>(11, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//PriorityQueue默认是小顶堆,实现大顶堆,需要反转默认排序器
return o2.compareTo(o1);
}
});
public static void Insert(Integer num) {
count ++;
if((count & 1) == 0) { //当两个堆中元素为奇数个,将新加入元素加入到小顶堆
//如果要加入的数据,比大顶堆的最大元素小,先将该元素插入大顶堆,然后将大顶堆的最大元素插入到小顶堆。
if(!maxHeap.isEmpty() && num < maxHeap.peek()){
maxHeap.offer(num);
num = maxHeap.poll();
}
minHeap.offer(num);
} else { //当两个堆中元素为偶数个,将新加入元素加入到大顶堆
//如果要加入的数据,比小顶堆的最小元素大,先将该元素插入小顶堆,然后将小顶堆的最小元素插入到大顶堆;
if(!minHeap.isEmpty() && num > minHeap.peek()) {
minHeap.offer(num);
num = minHeap.poll();
}
maxHeap.offer(num);
}
}
public static Double GetMedian() {
if(count == 0) {
throw new RuntimeException("no available number!");
}
double result;
//总数为奇数时,大顶堆堆顶就是中位数
if((count & 1) == 1){
result = maxHeap.peek();
} else {
result = (minHeap.peek() + maxHeap.peek())/2.0;
}
return result;
}
public static void main(String[] args) {
int [] test = {1,2,5,6,3,9,8};
Arrays.stream(test).forEach(e -> Insert(e));
System.out.println(GetMedian());
}
}
网友评论