数据流的中位数

数据流的中位数

问题定义: 不断有数字过来,问在当前所有数字中的中位数是多少

优先队列--堆

我们可以用一个大根堆和一个小根堆分别维护一个有序数,使得小根堆的所有数字都大于大根堆,这就要求小根堆的堆顶要大于等于大根堆的堆顶。
为了求得中位数,我们需要小根堆和大根堆的数字个数相等或相差一。
我们可以用优先队列实现如下:

priority_queue<int,vector<int>,greater<int> >l; //小根堆
priority_queue<int>g; //大根堆
void add(int num)
{
   l.push(num);
   g.push(l.top());
   l.pop();
   if(l.size()<g.size())
   {
      l.push(g.top());
      g.pop();
   }
}
int ask()
{
   return l.size()<g.size()?l.top():(l.top()+g.top())*0.5;
}

解法二--双指针

我们用multiset维护插入的有序集合,用l和r指针指向中位数,那么显然最终的结果是(l+r)*0.5。当数字个数为偶数时,l和r分别指向不同的两个相邻元素,否则指向同一个元素
代码如下:

multiset<int>s
multiset<int>::iterator left,right;
void add(int num)
{
   int n=s.size();
   s.insert(num);
   if(n==0)
      l=r=s.begin();
   else if(n&1) //原来有奇数个
   {
       if(num<(*l))
            l--;
       else if(num>(*l))
            r++;
   }
   else
   {
      if(num>*l&&num<*r)
        l++,r--;
      else if(num<=*l)
        l=--r;
      else
        l++;
   }
}
int ask()
{
    return (*l+*r)*0.5;
}

应用 LCP24

小扣在秋日市集入口处发现了一个数字游戏。主办方共有 N 个计数器,计数器编号为 0 ~ N-1。每个计数器上分别显示了一个数字,小扣按计数器编号升序将所显示的数字记于数组 nums。每个计数器上有两个按钮,分别可以实现将显示数字加一或减一。小扣每一次操作可以选择一个计数器,按下加一或减一按钮。

主办方请小扣回答出一个长度为 N 的数组,第 i 个元素(0 <= i < N)表示将 0~i 号计数器 初始 所示数字操作成满足所有条件 nums[a]+1 == nums[a+1],(0 <= a < i) 的最小操作数。回答正确方可进入秋日市集。

由于答案可能很大,请将每个最小操作数对 1,000,000,007 取余。

思路

我们对等式做一点小小的转化:nums[a]-a==nums[a+1]-(a+1),所以我们最终要得到的是所有的nums[i]-i的数字都相等。那么这个数很显然应该是这些数字的中位数
那么我们可以维护数据流的中位数,但是这样还要依此算下每个位置的代价(与中位数的距离),因此我们不妨直接维护两个堆中所有数字的和。
用l表示小根堆,r表示大根堆,让r的元素与l相等或多一,那么
当有奇数个数字时,代价是l元素的和sum[l]-nm+(n+1)m-sum[r]=sum[l]-sum[r]+m
当有偶数个数字时,代价是l元素的和sum[l]-nm+(n)m-sum[r]=sum[l]-sum[r]
代码如下:

class Solution {
public:
    typedef long long ll;
    const int mod=1e9+7;
    vector<int> numsGame(vector<int>& nums) {
        int n=nums.size();
        if(n==1)
            return {0};
        for(int i=0;i<n;i++)
            nums[i]-=i;
        priority_queue<int>g; //大根堆
        priority_queue<int,vector<int>,greater<int>>l; //小根堆
        vector<int>ans;
        l.push(max(nums[0],nums[1]));
        g.push(min(nums[0],nums[1]));
        ll sum0=g.top(),sum1=l.top();
        ans.push_back(0);
        ans.push_back(sum1-sum0);
        for(int i=2;i<n;i++)
        {
            if(nums[i]>l.top())
            {
                l.push(nums[i]);
                sum1+=nums[i];
            }
            else if(nums[i]<=g.top())
            {
                g.push(nums[i]);
                sum0+=nums[i];
            }
           // cout<<sum0<<" "<<sum1<<endl;
            if(l.size()+2==g.size())
            {
                l.push(g.top());
                sum0-=g.top();
                sum1+=g.top();
                g.pop();
            }
            else if(l.size()==g.size()+1)
            {
                g.push(l.top());
                sum1-=l.top();
                sum0+=l.top();
                l.pop();
            }
            //cout<<sum0<<" "<<sum1<<endl;
            ll val=(i%2)?(sum1-sum0):(sum1-sum0+g.top());
            ans.push_back(val%mod);
        }
        return ans;
    }
};
posted @ 2020-09-20 16:13  blueattack  阅读(238)  评论(0编辑  收藏  举报