[题解]剑指 Offer 41. 数据流中的中位数(C++)

题目

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[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、findMedian 进行 50000 次调用。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

这题考察的就是使用什么样的数据结构来存数据流的数并且排序。如果使用vector,那么每次插入新数排序都很麻烦,虽然利用二分查找确认要插入的位置只需要时间复杂度O(log(n)),但是需要将插入位置之后的元素全部往后移动;利用有序数据结构,我想到的是优先队列(堆),即STL中的priority_queue,关于priority_queue具体说明可以参考这里:https://en.cppreference.com/w/cpp/container/priority_queue
只用一个优先队列虽然可以解决插入数据的问题,但是要查找中位数却很麻烦,难道每次查找要pop掉一半的数据出来再插回去?解决方案也很简单:用两个优先队列。一个是大根堆,一个是小根堆,保证大根堆堆顶元素比小根堆堆顶元素还要小,同时两个堆的元素数量一样或者只相差1,如果它们数量一样,那么中位数就是两个堆堆顶元素的均值;如果不一样,那就是多1个的那个堆的堆顶元素。为了方便,可以在插入数据时设计保持其中一个堆的元素数量不少于另一个堆。
每次添加新元素的时间复杂度是O(n),查询中位数的时间复杂度则是O(1),空间复杂度O(n)。

代码

class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {

    }
    
    void addNum(int num) {
        small.push(num);
        large.push(small.top());
        small.pop();
        if(large.size() > small.size())
        {
            small.push(large.top());
            large.pop();
        }
    }
    
    double findMedian() {
        return small.size() > large.size() ? small.top() : (small.top() + large.top())/2.0;
    }

private:
    priority_queue<int, vector<int>, less<int>> small;
    priority_queue<int, vector<int>, greater<int>> large;
};

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

[题解]剑指 Offer 41. 数据流中的中位数(C++)

上一篇:[题解]LeetCode 1553. 吃掉 N 个橘子的最少天数(C++)(腾讯笔试题)


下一篇:Java //输入两个正整数m和n,求其最大的公约数和最小公倍数//12和20的最大公约数是4,最小公倍数是60