题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
分析:
数据是从数据流中读取出来的,数据是无序的,因此,我们需要一个容器将这些数据存储起来。数组,链表,时间复杂度都是O(n)的,一个是查询快,插入慢,另一个是查找慢,插入快。
二叉搜索树,平衡二叉树,都可以已O(logn)的时间复杂度处理数据,二叉搜索树可能会退化成O(n)。
我们可以用堆来实现,建立两个堆,第一个堆是大顶堆,第二个是小顶堆,保证大顶堆所有数据都要小于小顶堆中的数据。需要注意的是,我们要找的是中位数,那么两个堆中元素的个数的差值要小于等于1。当数据总数是偶数的时候,将数据放入小顶堆,否则放入大顶堆。
考虑两个特殊情况。
大顶堆里有数字3,小顶堆里有数字4,这时候,要插入一个数字2,按照前面的规则,会插入小顶堆,可是大顶堆有数据大于小顶堆的数据了。此时,我们把这个数据2插入大顶堆,再把大顶堆中的最大数据3拿出来插入小顶堆,此时就满足要求了。
大顶堆里有数字3,小顶堆里有数字4,5,这时候要插入一个6,这个6会插入大顶堆,可是大顶堆里有元素大于小顶堆了,此时,我们把这个数据6插入小顶堆,再把小顶堆中最小的数据4拿出来了插入大顶堆,此时就满足要求了。
解法:
package com.wsy;
import java.util.PriorityQueue;
import java.util.Random;
public class Main {
public static PriorityQueue<Integer> minHeap;
public static PriorityQueue<Integer> maxHeap;
public static void main(String[] args) {
readData();
getMedian();
}
public static void readData() {
minHeap = new PriorityQueue<Integer>((o1, o2) -> o1 - o2);
maxHeap = new PriorityQueue<Integer>((o1, o2) -> o2 - o1);
Random random = new Random();
for (int i = 0; i < 10; i++) {
int value = random.nextInt(100);
if ((i & 1) == 1) {// 奇数个,向大顶堆插入
if (!minHeap.isEmpty() && value > minHeap.peek()) {
minHeap.add(value);
value = minHeap.poll();
}
maxHeap.add(value);
} else {// 偶数个,向小顶堆插入
if (!maxHeap.isEmpty() && value < maxHeap.peek()) {
maxHeap.add(value);
value = maxHeap.poll();
}
minHeap.add(value);
}
}
}
public static void getMedian() {
int size = minHeap.size() + maxHeap.size();
if ((size & 1) == 1) {
System.out.println(maxHeap.peek() * 1.0);
} else {
System.out.println((maxHeap.peek() + minHeap.peek()) / 2.0);
}
}
}