数据流中的中位数(剑指offer第41题)

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

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

方法一:利用二分查找元素的插入位置,使所有元素升序排序。

利用lower_bound(),在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。

  • 时间复杂度:O(n)。O(logn)+O(n)≈O(n)。
  • 空间复杂度:O(n)。使用了数组保存输入。
class MedianFinder {
    vector store; // resize-able container
public:

    void addNum(int num)
    {
        if (store.empty())
            store.push_back(num);
        else
            store.insert(lower_bound(store.begin(), store.end(), num), num);   
    }

    double findMedian()
    {
        int n = store.size();
        return n & 1 ? store[n / 2] : (store[n / 2 - 1] + store[n / 2]) * 0.5;
    }
};

方法二:优先队列排序(大顶堆,小顶堆)

将中位数左边的数保存在大顶堆中,右边的数保存在小顶堆中。这样我们可以在O(1) 时间内得到中位数。

  • 时间复杂度O(logn)。堆插入和删除需要O(logn),查找中位数需要 O(1)。
  • 空间复杂度:O(n)。
class MedianFinder {
    priority_queue lo;                              // 大顶堆
    priority_queue, greater> hi;   // 小顶堆

public:
    // Adds a number into the data structure.
    void addNum(int num)
    {
        lo.push(num);                                    // 加到大顶堆

        hi.push(lo.top());                               // 平衡
        lo.pop();

        if (lo.size() < hi.size()) {                     // 维护两个堆元素个数
            lo.push(hi.top());
            hi.pop();
        }
    }

    double findMedian()
    {
        return lo.size() > hi.size() ? (double) lo.top() : (lo.top() + hi.top()) * 0.5;
    }
};

补充priority_queue知识点:

优先队列分两种:

  • 最大优先队列,无论入队顺序,当前最大的元素优先出队。
  • 最小优先队列,无论入队顺序,当前最小的元素优先出队。

事实上,优先队列的本质上是一个堆,它是一棵完全二叉树,分为小顶堆和大顶堆:

小顶堆是每一个根节点小于左右子节点的完全二叉树,堆顶元素最小,对应最小优先队列;

大顶堆是每一个根节点大于左右子节点的完全二叉树,堆顶元素最大,对应最大优先队列

优先队列函数声明如下:

priority_queue< type, container, function >

type:数据类型;container:实现优先队列的底层容器;function:元素之间的比较方式。

在STL中,默认情况下(不加后面两个参数)是以vector为容器,以 operator< 为比较方式,所以在只使用第一个参数时,优先队列默认是一个最大堆,每次输出的堆顶元素是此时堆中的最大元素。

priority_queue,greater > small_heap; //构造小顶堆

你可能感兴趣的:(数据流中的中位数(剑指offer第41题))