最大堆和最小堆求数据流中的中位数【java实现】



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
 }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值