您的位置:首页 > 编程语言 > Java开发

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

2016-05-04 16:25 513 查看

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

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: