import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class FindMedianInStream {
private static class Heap<T>{
//堆中元素存放的集合
private List<T> data;
//比较器
private Comparator<T> cmp;
//构造函数
public Heap(Comparator<T> cmp){
this.cmp=cmp;
this.data=new ArrayList<>(64);
}
//向上调整堆, idx,被上移元素的起始位置
public void shiftUp(int idx){
if(idx<0||idx>=data.size()){
throw new IllegalArgumentException(idx+"");
}
//获取开始调整的元素对象
T intentT=data.get(idx);
//如果不是根元素,则需要上移
while(idx>0){
int parentidx=(idx-1)/2;
T parentT=data.get(parentidx);
//上移条件,子节点比父节点大,此处定义的大是以比较器返回值为准
if(cmp.compare(intentT, parentT)>0){
//将父节点向下放
data.set(idx, parentT);
//记录父节点下放的位置
idx=parentidx;
//子节点不比父节点大,说明父子路径已经按从大到小排好顺序了,不需要调整了
}else{
break;
}
}
//index此时记录的是最后一个被下放的父节点的位置(也可能是自身)
//所以将最开始的调整的元素值放入index位置即可
data.set(idx, intentT);
}
//向下调整堆,idx被下移的元素的起始位置
public void shiftDown(int idx){
if(idx<0||idx>=data.size()){
throw new IllegalArgumentException(idx+"");
}
T intentT=data.get(idx);
int leftidex=idx*2+1;
while(leftidex<data.size()){
//取左节点的元素对象,并且假定其为2个子节点中最大的
T maxchild=data.get(leftidex);
//2个子节点中最大节点元素的位置,假定开始时为左子节点的位置
int maxidx=leftidex;
//获取右子节点的位置
int rightidx=leftidex+1;
if(rightidx<data.size()){
T rightchildT=data.get(rightidx);
//找出2个子节点中的最大子节点
if(cmp.compare(rightchildT, maxchild)>0){
maxchild=rightchildT;
maxidx=rightidx;
}
}
//如果最大子节点比父节点大,则需要向下调整
if(cmp.compare(maxchild, intentT)>0){
data.set(idx, maxchild);
idx=maxidx;
leftidex=2*idx+1;
}
//如果最大子节点不比父节点大,说明父子路径已经按照从大到小排好序了,不需要调整了
else{
break;
}
}
data.set(idx, intentT);
}
//添加一个元素
public void add(T item){
//添加元素到最后
data.add(item);
//上移,以完成重构
shiftUp(data.size()-1);
}
//删除堆顶节点
public T deleteTop(){
//如果堆已经为空,就抛出异常
if(data.isEmpty()){
throw new RuntimeException("The heap is empty.");
}
//获取堆顶元素
T firstT=data.get(0);
//删除最后一个元素
T lastT=data.remove(data.size()-1);
//删除元素后,如果堆为空的情况下,说明删除的元素也是堆顶元素
if(data.size()==0){
return lastT;
}else{
//将删除的元素放到堆顶
data.set(0, lastT);
//自上向下调整堆
shiftDown(0);
//返回堆顶元素
return firstT;
}
}
//获取堆顶节点,但不删除
public T gettop(){
if(data.isEmpty()){
throw new RuntimeException("The heap is empty.");
}
return data.get(0);
}
//获取堆的大小
public int size(){
return data.size();
}
//判断堆是否为空
public boolean isEmpty(){
return data.isEmpty();
}
//清空堆
public void clear(){
data.clear();
}
//获取堆中的所有数据
public List<T> getData(){
return data;
}
}
/**
* 升序比较器
* @param args
*/
private static class IncComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o1-o2;
}
}
/**
* 降序比较器
* @param args
*/
private static class DescComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2-o1;
}
}
private static class DynamicArray{
private Heap<Integer> max;
private Heap<Integer> min;
public DynamicArray(){
max=new Heap<>(new IncComparator());
min=new Heap<>(new DescComparator());
}
//插入数据
public void insert(Integer num){
//已经有偶数个数据了(可能没有数据)
//数据总是偶数个时把新数据插入到小堆中
if((min.size()+max.size())%2==0){
//大堆中有数据,并且插入的元素比大堆中的元素小
if(max.size()>0 && num<max.gettop()){
//将num插入到大堆中
max.add(num);
//删除堆顶元素,大堆中的最大元素
num=max.deleteTop();
}
min.add(num);
}
//数据总是奇数个时把新数据插入到大堆中
else{
//小堆中有数据,并且插入的元素比小堆中的元素大
if(min.size()>0 && num>min.size()){
min.add(num);
num=min.deleteTop();
}
max.add(num);
}
}
public double getMedian(){
int size=max.size()+min.size();
if(size==0){
throw new RuntimeException("No number is available");
}
if((size&1)==1){
return min.gettop();
}else{
return (max.gettop()+min.gettop())/2.0;
}
}
}
public static void main(String[] args) {
DynamicArray array = new DynamicArray();
array.insert(5);
System.out.println(array.getMedian()); // 5
array.insert(2);
System.out.println(array.getMedian()); // 3.5
array.insert(3);
System.out.println(array.getMedian()); // 3
array.insert(4);
System.out.println(array.getMedian()); // 3.5
array.insert(1);
System.out.println(array.getMedian()); // 3
array.insert(6);
System.out.println(array.getMedian()); // 3.5
array.insert(7);
System.out.println(array.getMedian()); // 4
array.insert(0);
System.out.println(array.getMedian()); // 3.5
array.insert(8);
System.out.println(array.getMedian()); // 4
}
}