数据流中的中位数(剑指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<int> 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<int> lo;                              // 大顶堆
    priority_queue<int, vector<int>, greater<int>> 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<int,vector<int>,greater<int> > small_heap; //构造小顶堆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值