• 剑指Offer_#41_ 数据流中的中位数


    剑指Offer_#41_ 数据流中的中位数

    Contents

    题目

    如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
    例如,
    [2,3,4] 的中位数是 3
    [2,3] 的中位数是 (2 + 3) / 2 = 2.5
    设计一个支持以下两种操作的数据结构:
    void addNum(int num) - 从数据流中添加一个整数到数据结构中。
    double findMedian() - 返回目前所有元素的中位数。
    示例 1:

    输入:
    ["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
    [[],[1],[2],[],[3],[]]
    输出:[null,null,null,1.50000,null,2.00000]

    示例 2:

    输入:
    ["MedianFinder","addNum","findMedian","addNum","findMedian"]
    [[],[2],[],[3],[]]
    输出:[null,null,2.00000,null,2.50000]

    限制:
    最多会对 addNum、findMedia进行 50000 次调用。

    思路分析

    借助Java中的优先队列PriorityQueue,分别创建大顶堆和小顶堆,大顶堆保存数据流中较小的一半数字,小顶堆保存数据流中较大的一半数字。如下图所示。

    将所有数字正确地加入到大顶堆和小顶堆之后,再根据数据流长度的奇偶来计算中位数。
    关键在于如何将数字正确的加入大顶堆和小顶堆呢?每一次输入的数字是混乱无序的,如何知道当前输入的数字是属于较小的一半还是较大的一半?
    有一个很巧妙的方法,就是不用直接选择当前数字加入到大顶堆还是小顶堆,而是大顶堆和小顶堆互相配合,互相“倒腾”数据。
    具体来说,如果我想在大顶堆加入一个数字,并不是直接把当前输入的数字加入,而是先把当前输入的数字加入小顶堆,然后弹出小顶堆当中最小的数字,加入大顶堆。反之同理。
    这样的方法可以保证,大顶堆里边的数字比小顶堆里的数字要小。

    解答

    class MedianFinder {
        Queue<Integer> A,B;
        /** initialize your data structure here. */
        public MedianFinder() {
            A = new PriorityQueue<>();//小顶堆,保存较大的一半
            B = new PriorityQueue<>((x,y) -> y-x);//大顶堆,保存较小的一半
        }
        
        public void addNum(int num) {
            if(A.size() == B.size()){
                //先加入A,再加入B,如果长度相同,说明上次加入的是B,这次该加入A
                //先把num加入B,再把B的顶弹出加入到A(即把B中最大的加入A)
                B.add(num);
                A.add(B.poll());
            }else{
                //同理,先把num加入A,再把A的顶弹出加入到B(把A中的最小加入到B)
                A.add(num);
                B.add(A.poll());
            }
        }
        
        public double findMedian() {
            //整个数据流长度为偶数
            if(A.size() == B.size()) return (A.peek() + B.peek()) / 2.0;
            //整个数据流长度为奇数,那么中位数就是A的顶
            else return A.peek();
        }
    }
  • 相关阅读:
    HTML和XHTML知识总结
    理解margin-left:-100%
    git clean的用法
    vue路由传参的三种基本方式
    vertical-align属性
    纯CSS制作各种图形(多图预警)
    css伪元素:before和:after用法详解
    前端注册登录的业务流程
    Vue-cli 中为单独页面设置背景图片铺满全屏的方法
    vscode 开启对 webpack alias(文件别名) 引入的智能提示
  • 原文地址:https://www.cnblogs.com/Howfars/p/13304132.html
Copyright © 2020-2023  润新知