对顶堆(大根堆+小根堆)优先队列

C - Running Median (HDU - 3282)

For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read, output the median (middle value) of the elements received so far.

Input
The first line of input contains a single integer P, (1 ≤ P ≤ 1000), which is the number of data sets that follow. The first line of each data set contains the data set number, followed by a space, followed by an odd decimal integer M, (1 ≤ M ≤ 9999), giving the total number of signed integers to be processed.
The remaining line(s) in the dataset consists of the values, 10 per line, separated by a single space.
The last line in the dataset may contain less than 10 values.

Output
For each data set the first line of output contains the data set number, a single space and the number of medians output (which should be one-half the number of input values plus one). The output medians will be on the following lines, 10 per line separated by a single space. The last line may have less than 10 elements, but at least 1 element. There should be no blank lines in the output.

Sample Input

3
1 9
1 2 3 4 5 6 7 8 9
2 9
9 8 7 6 5 4 3 2 1
3 23
23 41 13 22 -3 24 -31 -11 -8 -7
3 5 103 211 -311 -45 -67 -73 -81 -99
-33 24 56

Sample Output

1 5
1 2 3 4 5
2 5
9 8 7 6 5
3 12
23 23 22 22 13 3 5 5 3 -3
-7 -3

题目大意
多组输入,每当输入的个数为奇数时输出此时排序好的中位数。

题目分析
使用优先队列的方法建立对顶堆,保持两个二叉堆的数据个数相差不大于 1。当输入个数为奇数时,输出数据个数较大的那一个的 top 元素。

两个二叉堆的堆顶其中之一必为中位数,因为小根堆小到大,大根堆
从大到小,将堆顶放在一起相当于是将一个有序序列从中间断开,断开位置即为中位数。

在这里插入图片描述

一般对顶堆是大根堆在上,小根堆在下,而我相反(哎呀无所谓了,做出题就行),这个玩意是可以动态维护第k大的值(比如中位数)
每次操作logn,注意小根堆里面最小的还要比大根堆里面最大的要大。

首先把第一个数放入小根堆里面,第二个数进行判断,如果小于小根堆,就放入大根堆里面,否则还放入小根堆里面。注意,每放入一次,都要判断,如果小根堆元素个数比大根堆多二,就把小根堆top放入大根堆里面,然后删掉小根堆这个top,相当于把这个元素下移了一位。
每次i为奇数时,输出小根堆里面的top,这个top就是中位数。

错误代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;

int n,cnt,len,d,x,jishu;

priority_queue<int> max_heap;	//大根堆建立
priority_queue<int,vector<int>,greater<int>> min_heap; //小根堆建立

int main()
{
	cin>>n;
	while(n--)
	{
		jishu=0;
		cin>>cnt>>len;
		cout<<cnt<<" "<<(len+1)/2<<endl;
		for(int i=1;i<=len;i++)
		{
			cin>>x;
			if(i==1) min_heap.push(x); //第一个放入小根堆
			else
			{
				if(x<min_heap.top()) max_heap.push(x); 
				else min_heap.push(x);
				if(max_heap.size()>min_heap.size())
				{
					min_heap.push(max_heap.top());
					max_heap.pop();
				}
				if(min_heap.size()-max_heap.size()>2)
				{
					max_heap.push(min_heap.top()); 
					min_heap.pop();
				}
				
			}	
			
			if(i%2==1)
			{
				jishu++;
				if(jishu%10!=1) cout<<" ";
				cout<<min_heap.top();
				if(jishu%10==0) cout<<'\n';
			}
		}
		while(!max_heap.empty()) max_heap.pop(); //清空堆
		while(!min_heap.empty()) min_heap.pop();
		if(jishu%10!=0) cout<<endl;
	}
    
    return 0;
}

实际上,我的代码中间对空队列用了size是错误的,空队列不能用size,top,可以在自己编译环境下试一试,是行不通的,但是好像也能过,嘿嘿。

一个队列首先要对其进行是否为empty的判断,再进行top,size的使用。

修改后代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;

int n,cnt,len,d,x,jishu;

priority_queue<int> max_heap;
priority_queue<int,vector<int>,greater<int>> min_heap;

int main()
{
	cin>>n;
	while(n--)
	{
		jishu=0;
		cin>>cnt>>len;
		cout<<cnt<<" "<<(len+1)/2<<endl;
		for(int i=1;i<=len;i++)
		{
			cin>>x;
			
			//修改处开始
			if(i%2==1)
			{
				if(max_heap.empty()) min_heap.push(x);
				else
				{
					if(x>max_heap.top()) min_heap.push(x);
					else min_heap.push(max_heap.top()),max_heap.pop(),max_heap.push(x);
				} 
			}
			else
				{
					if(x<min_heap.top()) max_heap.push(x);
					else max_heap.push(min_heap.top()),min_heap.pop(),min_heap.push(x);  
				}
				//修改处截至
				
			
			if(i%2==1)
			{
				jishu++;
				if(jishu%10!=1) cout<<" ";
				cout<<min_heap.top();
				if(jishu%10==0) cout<<'\n';
			}
		}
		while(!max_heap.empty()) max_heap.pop();
		while(!min_heap.empty()) min_heap.pop();
		if(jishu%10!=0) cout<<endl;
	}
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值