• 数据流的中位数


    数据流的中位数

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

    优先队列--堆

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

    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;
        }
    };
    
  • 相关阅读:
    sudo: no tty present and no askpass program specified 解决方法
    中间件kingshard入门(一):基本安装
    mysql主从配置
    haproxy+keepalive双主高可用实现负载均衡
    lvs负载均衡配置
    docker可视化集中管理工具shipyard安装部署
    安装nodejs
    redis数据导入与导出以及配置使用
    搭建ss5环境
    thinkphp5+python.apscheduler实现计划任务
  • 原文地址:https://www.cnblogs.com/flightless/p/13700780.html
Copyright © 2020-2023  润新知