poj3784 Running Median(对顶堆)

题意

动态维护中位数问题:依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。


我的想法

中位数是第(n+1)/2大的数,也可以看成是排完序后中间位置的数。
再确定了一个中位数后,每加入一个新数,就用一个二分插入排序将其放入中位数左或右,这样会涉及到链表上的二分查找,我不会。

其实可以用vector容器代替链表的,可以一试。


题解

对顶堆

聪明的解题人,在中位数这里把序列折成两半,关注中位数附近的数,这样两半都是递增的。1~n/2我们关注它的最大值,用大根堆;n/2+1~n关注它的最小值,用小根堆。
对顶堆就是建立两个堆,保持两堆的大小。如果一堆太大,则取出堆顶放入另一堆。

插入时,如果大于中位数,则将其放在小根堆中,否则放在大根堆。小根堆的堆顶就是我们要的中位数。然后维护这个对顶堆即可。


代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;


priority_queue<int,vector<int>,less<int> > da;int szd;
priority_queue<int,vector<int>,greater<int> > xiao;int szx;


int wc,w[20];
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		wc=0;
		while(!da.empty()) da.pop();szd=0;
		while(!xiao.empty()) xiao.pop();szx=0;
		
		int n;scanf("%d",&n);printf("%d ",n);
		scanf("%d",&n);printf("%d\n",(n+1)/2);
		int mid=-1e9;
		for(int i=1;i<=n;i++)
		{
			int x;scanf("%d",&x);
			if(x>mid) xiao.push(x),szx++;//插入 
			else da.push(x),szd++;
			if(i&1)//是奇数 
			{
				while(szd+1!=szx)//调整两堆大小 
				{
					if(szd+1>szx)
					{
						int t=da.top();
						da.pop();szd--;
						xiao.push(t);szx++;
					}
					else
					{
						int t=xiao.top();
						xiao.pop();szx--;
						da.push(t);szd++;
					}
				}
				mid=xiao.top();//小根堆的堆顶就是中位数
				w[++wc]=mid;
				if(wc==10)//10个10个地输出 
				{
					for(int i=1;i<wc;i++) printf("%d ",w[i]);
					printf("%d\n",w[wc]);
					wc=0;
				}
			}
		}
		if(wc>0)
		{
			for(int i=1;i<wc;i++) printf("%d ",w[i]);
			printf("%d\n",w[wc]);
			wc=0;
		}
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/a_bright_ch/article/details/80998878
今日推荐