数据流的中位数
数据流的中位数
问题定义: 不断有数字过来,问在当前所有数字中的中位数是多少
优先队列--堆
我们可以用一个大根堆和一个小根堆分别维护一个有序数,使得小根堆的所有数字都大于大根堆,这就要求小根堆的堆顶要大于等于大根堆的堆顶。
为了求得中位数,我们需要小根堆和大根堆的数字个数相等或相差一。
我们可以用优先队列实现如下:
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;
}
};