package day5;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
/*
* 问题:
* 如何得到一个数据流中的中位数?
* 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
* 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
*
* 思路:
* 采用堆,一个大根堆,一个小根堆。(小根堆存放偏大得数,大根堆存放偏小的数)
* 第一个数先读到大根堆中,
* 后面的数,对比大根堆堆顶,如果小于等于堆顶放入大根堆,
* 否则尝试放入小根堆,如果小根堆空,放入,否者对比小根堆堆顶,大于堆顶放入小根堆。否则放入大根堆。
*
* 如果两个堆相差>1;取出堆顶,放到数目少的里面。
*
*/
public class Code04_MedianInDatastream {
public static class MedianHolder{
PriorityQueue<Integer> minHeap;
PriorityQueue<Integer> maxHeap;
public MedianHolder(){
this.minHeap = new PriorityQueue<>();
this.maxHeap = new PriorityQueue<>(new MaxHeapComparator());
}
public void modify() {
int count = maxHeap.size() - minHeap.size();
if (Math.abs(count) >1) {
if(count >0) {
minHeap.add(maxHeap.poll());
}else {
maxHeap.add(minHeap.poll());
}
}
}
//naive!!!!!要考虑全面!!为空呢?
public void add (int num) {
if(maxHeap.isEmpty()) {
maxHeap.add(num);
return ;
}
if(num <= maxHeap.peek()) {//注意这里是小于大根堆堆顶
maxHeap.add(num);
}else {//否者尝试放入小根堆,尝试!!!尝试!!!
if(minHeap.isEmpty()) {
minHeap.add(num);
return ;
}
if(num >= minHeap.peek()) {
minHeap.add(num);
}else {
maxHeap.add(num);
}
}
modify();
}
public Integer getMedian() {
int maxHeapSize = maxHeap.size();
int minHeapSize = minHeap.size();
if (maxHeap.isEmpty() && minHeap.isEmpty()) {
return null;
}
Integer maxHeapHead = maxHeap.peek();
Integer minHeadHead = minHeap.peek();
if(((maxHeapSize + minHeapSize) & 1) == 0){
return (maxHeapHead + minHeadHead)/2;
}
return minHeapSize > maxHeapSize ? minHeadHead :maxHeapHead;
}
}
public static class MaxHeapComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2 -o1;
}
}
// for test
public static int[] getRandomArray(int maxLen, int maxValue) {
int[] res = new int[(int) (Math.random() * maxLen) + 1];
for (int i = 0; i != res.length; i++) {
res[i] = (int) (Math.random() * maxValue);
}
return res;
}
// for test, this method is ineffective but absolutely right
public static int getMedianOfArray(int[] arr) {
int[] newArr = Arrays.copyOf(arr, arr.length);
Arrays.sort(newArr);
int mid = (newArr.length - 1) / 2;
if ((newArr.length & 1) == 0) {
return (newArr[mid] + newArr[mid + 1]) / 2;
} else {
return newArr[mid];
}
}
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
boolean err = false;
int testTimes = 200000;
for (int i = 0; i != testTimes; i++) {
int len = 30;
int maxValue = 1000;
int[] arr = getRandomArray(len, maxValue);
MedianHolder medianHold = new MedianHolder();
for (int j = 0; j != arr.length; j++) {
medianHold.add(arr[j]);
}
if (medianHold.getMedian() != getMedianOfArray(arr)) {
err = true;
printArray(arr);
System.out.println(medianHold.getMedian());
break;
}
}
System.out.println(err ? "Oops..what a fuck!" : "today is a beautiful day^_^");
}
}