流求中位数
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^_^"); } }