使用堆的方式获取数据流中的中位数

题目:设计一个方法,要求这个方法可以不断输出一个数据流中的中位数(数据流中的数字整型并且随机)

思想:构建两个小根堆,其中一个小根堆存储的数据相对较小(堆B),另外一个小根堆存储的数据较大(堆A),并且构建两个变量,其中一个存储堆B的最大值,另一个存储堆A的最小值。每次接收一个数字的时候,先与堆A中的最小值比较;如果这个数字大于堆A中的最小值,则存储在堆A中。然后调整堆A和堆B中的数据量,使得他它们中的数据数目小于等于1。最后调整堆A为小根堆。如果这个数据小于堆A中最小值,则将这个数据存储在堆B中,然后调整堆A和堆B中的数据量,使得他它们中的数据数目小于等于1。调整完之后,将堆B调整为小根堆,并且同步堆B的最大值。输出:如果两个堆中的数据量相等,则中位数就是堆A的最小值和堆B的最大值相加除以2,如果堆A的数据量多于堆B,则中位数就是堆A的最小值,如果堆B的数据量多于堆A,则中位数就是堆B的最大值。

public class MedianRealTime {

    //存储大数据的小根堆(堆A)
    List<Integer> Max_heap = new ArrayList<>();
    //存储小数据的小根堆(堆B)
    List<Integer> Min_heap = new ArrayList<>();

    public int MaxHeap_min; //A堆的最小值
    public int MinHeap_Max; //B堆的最大值
    public static final int Max_Value = Integer.MAX_VALUE;

    /**
     * 实时获取中位数的方法
     * @param data
     * @return
     */
    public int getMiddleData(int data) {

        //第一个数据进来存储在A堆,并且将A堆的最小值设置为第一个进来的数据
        if (Max_heap == null) {
            Max_heap.add(data);
            MaxHeap_min = data;
        } else {
            balanceTwoHeaps(data);
        }
        //如果两个堆中的数据数目相等,那么中位数就是A堆的最小值与B堆的最大值除以2
        if (Max_heap.size() == Min_heap.size()) {
            return (MaxHeap_min + MinHeap_Max) / 2;
            //如果A堆比B堆中数据的数目多,那么中位数就是A堆中的最小值
        } else if (Max_heap.size() > Min_heap.size()) {
            return MaxHeap_min;
            //如果B堆比A堆中数据的数目多,那么中位数就是B堆中的最大值
        } else
            return MinHeap_Max;
    }

    /**
     * 新加入一个数之后,使得两个堆的长度之差不超过1
     * @param data
     */

    private void balanceTwoHeaps(int data) {
        //如果后来进入的数据大于A堆的最小值,则将这个数放在A堆
        if (data > MaxHeap_min) {
            Max_heap.add(data);
            //调整两个堆中数据的数目,使两个堆中数据的数目的绝对值小于等于1
            if (Math.abs(Max_heap.size() - Min_heap.size()) > 1) {
                //将大根堆的最小值取出来
                int x = MaxHeap_min;
                //移除大根堆的最小值
                Max_heap.remove(1);
                //将这个最小值加入到小根堆里面
                Min_heap.add(x);
                //同步大根堆的最小值
                MaxHeap_min = x;
            }
            //调整A堆为小根堆
            HeapSort(Max_heap);
            //如果后来进入的数据小于A堆的最小值,则将这个数放在B堆
        } else {
            Min_heap.add(data);
            //调整两个堆中数据的数目,直到两个堆中数据的数目的绝对值小于等于1
            if (Math.abs(Max_heap.size() - Min_heap.size()) > 1) {
                //将小根堆的最大值取出来
                int x = MinHeap_Max;
                //并将它移除
                Min_heap.remove(Min_heap.size() - 1);
                //将这个数添加到A堆
                Max_heap.add(x);
                //同步A堆的最小值
                MaxHeap_min = x;
                HeapSort(Max_heap);
            }
            //调整完之后,将B堆调整为小根堆
            HeapSort(Min_heap);
            //并且同步B堆的最大值
            MinHeap_Max = Min_heap.get(Min_heap.size() - 1);
        }
    }


    /**
     * 堆排序方法采用大根堆排序
     * 首先构建大根堆,使得根元素为最大的,
     * 堆排序时,将根元素与最后一个元素进行交换,再对前n-1个元素进行堆排序
     *
     * @param list
     * @return
     */
    private static List<Integer> buildMaxHeap(List<Integer> list) {
        if (list.size() <= 1)
            return list;
        for (int i = (list.size() - 2) / 2; i >= 0; i--)

        {
            adjust(list, i, list.size());
        }
        return list;
    }


    private static void adjust(List<Integer> list, int k, int length) {
        int tmp = list.get(k);
        for (int i = 2 * k + 1; i < length; i = 2 * i + 1) {
            if (i + 1 < length && list.get(i) < list.get(i + 1))
                i++;
            if (tmp > list.get(i)) {
                break;
            } else {
                list.set(k, list.get(i));
                k = i;
            }
        }
        list.set(k, tmp);
    }

    private static void HeapSort(List<Integer> list) {
        List<Integer> a = buildMaxHeap(list);
        for (int i = a.size() - 1; i >= 0; i--) {
            int tmp = a.get(0);
            a.set(0, a.get(i));
            a.set(i, tmp);
            adjust(a, 0, i);
        }
    }
}

这个方法空间复杂度相对来说比较高,因此有优化的地方可以降低空间复杂度。

优化后的总体思路:对于每个不同数字构造一个节点。这个节点具有两个成员属性,其中一个是Value(数据),一个是Num(数据出现的次数)。每次接收一个数据,原来是直接比较后存储堆中,现在就是多了一个步骤,需要首先查找堆看是否已经存在关于这个数据的节点。还有一点,就是其实完全没有必要将两个堆中的数据量调整得非得相等,或者非得相差至多为1。可以适当地加大差值,只要求出中位数即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值