剑指offer之数据流中的中位数

本文介绍了一种高效计算数据流中位数的方法,利用最大堆和最小堆维持数据平衡,确保快速查找中位数。文章详细解释了插入数据及调整堆的操作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

解题思路

问题本身不难,关键在于采取哪种数据结构和算法:
(1)如果用未排序的数组实现,那么插入的时间复杂度为O(1),查找的时间复杂度为O(n)(半快速排序法);
(2)如果是排序的数组,那么插入的时候要维护顺序,插入的时间开销要大一些,为O(n),查找时间复杂度为O(1);
(3)用排序的链表实现,时间复杂度和排序的数组情况完全一样;
(4)二叉搜索树可以使插入操作更快一点,时间复杂度为O(logn),查找操作为O(logn)。但是在树极度不平衡的情况下这两种操作时间复杂度都是O(n);
(5)还可以用AVL树实现。

作者用到的方法非常巧妙,注意到我们只需要位于中间的两个数就可以得出中位数,当元素个数为奇数个时可以看成这两个数一样。中位数在前半部分它是最大的,右半部分是最小的。我们只要始终知道前半部分数里的最大的那个数,后半部分里那个最小的数就可以了,其他的不用关心。这明显是堆的功用,前半部分用一个最大堆,后半部分建一个最小堆中位数要保持两个堆的数目之差不超过1。为了实现平均分配,可以在数据的总数目是偶数时把新数据插入到最小堆中,否则插入最大堆中。

另一个问题是保持最大堆的所有数据都小于最小堆中的数据。如果插入一个数使得总数目和为偶数,根据之前的原则应该插入最小堆中,但是新插入的元素可能比最大堆中的元素小怎么办?这就违反了最小堆的数据一定大于最大堆得原则。实际上这里插入的元素应该是新元素和最大堆中所有元素的最大值,最大堆正好有返回一个最大值的作用,所以新插入的元素先插入最大堆,然后取出最大值再插入最小堆中。如果插入元素使得数据总数目和为奇数,处理方法类似。

优先队列

class Solution {
public:
    //用一个大顶堆和一个小顶堆,维持大顶堆的数都小于等于小顶堆的数,且两者的个数相等或差1。平均数就在两个堆顶的数之中。
    priority_queue<int, vector<int>, less<int> > p;
    priority_queue<int, vector<int>, greater<int> > q;
    void Insert(int num)
    {
        if(p.empty() || num <= p.top()) p.push(num);
        else q.push(num);
        if(p.size() == q.size() + 2) q.push(p.top()), p.pop();
        if(p.size() + 1 == q.size()) p.push(q.top()), q.pop();
    }

    double GetMedian()
    {
        return p.size() == q.size() ? (p.top() + q.top()) / 2.0 : p.top();
    }

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值