题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
解题思路:
用两个堆,一个大顶堆,一个小顶堆来过滤数据。
import java.util.ArrayList;
import java.util.Comparator;
/**
* 数据流中的中位数
*
* @author 过路的守望
*
*/
public class MedianNumber {
/*
* 最大堆
*/
private Heap maxHeap = new Heap(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
/*
* 最小堆
*/
private Heap minHeap = new Heap(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
/*
* 插入元素
*/
public void Insert(Integer num) {
/*
* 如果最大堆中的元素个数大于等于最小堆中的元素个数,则新元素插入到最小堆中
*/
if (maxHeap.getSize() >= minHeap.getSize()) {
/*
* 如果最小堆中未插入元素,则新元素直接插入
*/
if (minHeap.getSize() == 0) {
minHeap.add(num);
}
/*
* 若最小堆中已存在元素,则将带插入元素与最大堆中的堆顶元素相比较,若待插入元素较小,则将最大堆堆顶元素弹出后添加到最小堆中,
* 最大堆中将待插入元素添加进去。
*/
else if (num < maxHeap.peek()) {
minHeap.add(maxHeap.pop());
maxHeap.add(num);
}
/*
* 待插入元素不小于最大堆堆顶元素
*/
else {
minHeap.add(num);
}
} else {
/*
* 如果最大堆中未插入元素,则新元素直接插入
*/
if (maxHeap.getSize() == 0) {
maxHeap.add(num);
}
/*
* 若最大堆中已存在元素,则将带插入元素与最小堆中的堆顶元素相比较,若待插入元素较大,则将最小堆堆顶元素弹出后添加到最大堆中,
* 最小堆中将待插入元素添加进去。
*/
else if (num > minHeap.peek()) {
maxHeap.add(minHeap.pop());
minHeap.add(num);
}
/*
* 待插入元素不大于最小堆堆顶元素
*/
else {
maxHeap.add(num);
}
}
}
/*
* 得到中间值
*/
public Double GetMedian() {
/*
* 判断数据数目奇偶
*/
int size = maxHeap.getSize() + minHeap.getSize();
if ((size & 1) == 1) {
return (double) minHeap.peek();
}
return (maxHeap.peek() + minHeap.peek()) / 2.0;
}
}
/**
* 数据结构-堆
*
* @author 过路的守望
*
*/
class Heap {
/*
* 比较器
*/
private Comparator<Integer> comparator;
private ArrayList<Integer> list;
public Heap() {
list = new ArrayList<Integer>();
}
/*
* 构造器传入比较器
*/
public Heap(Comparator<Integer> comparator) {
this();
this.comparator = comparator;
}
/*
* 弹出堆顶元素
*/
public int pop() {
int data = list.get(0);
list.set(0, list.remove(list.size() - 1));
percolateDown();
return data;
}
/*
* 返回堆顶元素
*/
public int peek() {
return list.get(0);
}
/*
* 添加元素
*/
public void add(int element) {
list.add(element);
percolateUp();
return;
}
/*
* 堆中对象个数
*/
public int getSize() {
return list.size();
}
/*
* 下滤操作
*/
private void percolateDown() {
int size = list.size();
/*
* 从下标为0的节点开始下滤
*/
int i = 0;
/*
* temp保存最大堆或最小堆的堆顶值
*/
int temp = list.get(0);
int leftChild = getLeftChild(i);
while (leftChild < size) {
/*
* 得到左右儿子中较大的下标
*/
if (leftChild < size - 1
&& comparator.compare(list.get(leftChild),
list.get(leftChild + 1)) < 0) {
leftChild++;
}
/*
* 若儿子大于父亲,就把 儿子的值赋给父亲
*/
if (comparator.compare(temp, list.get(leftChild)) < 0) {
list.set(i, list.get(leftChild));
i = leftChild;
leftChild = getLeftChild(i);
continue;
} else {
break;
}
}
/*
* 下滤完成,找到temp所在下标
*/
list.set(i, temp);
}
/*
* 上滤操作
*/
private void percolateUp() {
/*
* 从堆中最后一个元素开始上滤
*/
int i = list.size() - 1;
int temp = list.get(i);
int parent = getParent(i);
while (parent >= 0) {
/*
* 若父亲小于儿子,则把父亲的值赋给儿子
*/
if (comparator.compare(temp, list.get(parent)) > 0) {
list.set(i, list.get(parent));
i = parent;
parent = getParent(parent);
continue;
} else {
break;
}
}
/*
* 上滤完成,找到temp所在下标
*/
list.set(i, temp);
}
/*
* 左儿子下标
*/
private int getLeftChild(int i) {
return 2 * i + 1;
}
/*
* 父亲下标
*/
private int getParent(int i) {
return (int) Math.floor((i - 1) / 2.0);
}
}