数据流中的中位数

题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
解题思路:
用两个堆,一个大顶堆,一个小顶堆来过滤数据。

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值