295. 数据流的中位数

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
进阶:

如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?

本题的难点就在于如何优化你的算法,你可以每次添加数都进行一次排序,然后find可以直接拿出来,用下标获取。你也可以每次插入都是用插入排序来,这样自然就是有序的,但是这些算法,都复杂度过高。
还有一种简单的方法,需要用到一个数据结构,优先队列,优先队列的底层是用堆来实现的,本道题用两个一个大顶堆,一个小顶堆就能完美解决。

我们要找
1 2 3 4 5 这个的中位数。
我们可以把这个数组一分为2,一份拿1、2、3 一份拿4、5。 一个是升序排序,一个是降序排序,当整个数组大小是奇数的时候我们取大顶堆的堆顶,当数组是偶数的时候我们取大顶堆和小顶堆的平均数。
我们每次添加数字,都需要它经过两个堆排序,最后把他放到小顶堆里去,我们要确保是奇数的时候大顶堆比小顶堆多一个(这取决于我的算法实现,你也可以让小顶堆比大顶堆多一个)。 那就是判断当加入这个数字后如果是奇数,那就需要把小顶堆堆顶的数放到大顶堆里去。
每次插入大顶小顶堆都是logn的复杂度,那我们就成功优化了。
本道题是比较难的,很难想到用堆,就算想到了,也很难实现数字的流动操作。但是一旦想到了,代码就短短的十来行。
代码如下:

class MedianFinder {
    private int count;
    PriorityQueue  minHeap;
    PriorityQueue  maxHeap;

    /** initialize your data structure here. */
    public MedianFinder() {
        int count = 0;
        minHeap = new PriorityQueue<>();
        maxHeap = new PriorityQueue<>( (x,y) -> (y-x));
    }
    
    public void addNum(int num) {
        count++;
        maxHeap.offer(num);
        minHeap.offer(maxHeap.poll());
        if (count % 2 != 0){
            maxHeap.offer(minHeap.poll());
        }

    }
    
    public double findMedian() {
        if (count % 2 == 0){
            return  (double)(minHeap.peek() + maxHeap.peek()) / 2.0;
        }
        else 
            return maxHeap.peek();
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-median-from-data-stream
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

你可能感兴趣的:(295. 数据流的中位数)