对顶堆学习总结

对顶堆介绍

对顶堆是由一个大顶堆和一个小顶堆组合而成的数据结构

用途

与传统堆维护最大数不同,对顶堆用于动态维护第k大的数(或者第k小的数)

模版 

比如说要求第k大的值

小顶堆存放前k个大的数,第k的大的数,在小顶堆的堆顶

大顶堆存放比第k个数都小的元素 

1.建立大顶堆和小顶堆

priority_queue<int> qmax;  //相当于大顶堆
priority_queue<int,vector<int>,greater<int>> qmin;  //相当于小顶堆

2.如果小顶堆是空的,或者添加的数比小顶堆小,那么就将其存进小顶堆 

if(qmin.empty()||a>=qmin.top())
		qmin.push(a);

 3.如果小顶堆的值不够k个,那么就将大顶堆的值存进小顶堆,反之则是将小顶堆的堆顶值弹出,放进大顶堆

while(qmin.size()<k)
		{
			qmin.push(qmax.top());
			qmax.pop();
		}
		while(qmin.size()>k)
		{
			qmax.push(qmin.top());
			qmin.pop();
		}

4.查询,直接查询小顶堆的堆顶元素

cout<<qmin.top()<<"\n";

 

例题

P1168 中位数

这个就是一个对顶堆的纯模版题,我们既可以从中间大,也可以从中间小入手 

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int a;
int k;
priority_queue<int> qmax;  //相当于大顶堆
priority_queue<int,vector<int>,greater<int>> qmin;  //相当于小顶堆
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		if(i%2==1)
		{
			k=(i+1)/2;
		}
		cin>>a;
		if(qmin.empty()||a>=qmin.top())
		qmin.push(a);
		else
		qmax.push(a);
		while(qmin.size()<k)
		{
			qmin.push(qmax.top());
			qmax.pop();
		}
		while(qmin.size()>k)
		{
			qmax.push(qmin.top());
			qmin.pop();
		}
		if(i%2==1)
		cout<<qmin.top()<<"\n";
	}
	return 0;
}

P7072 [CSP-J2020] 直播获奖 

也是一个板子题,只不过是将前k个都输出而已

#include<bits/stdc++.h>
using namespace std;
#define int long long
 
int n,w;
priority_queue<int> qmax;  //相当于大顶堆
priority_queue<int,vector<int>,greater<int> > qmin;//相当于小顶堆
int a,k;
signed main()
{
	cin>>n>>w;
	for(int i=1;i<=n;i++)
	{
		k=max(i*w/100,1LL);
		cin>>a;
		if(qmin.empty()||a>=qmin.top())
		qmin.push(a);
		else
		qmax.push(a);
		while(qmin.size()<k)
		{
			qmin.push(qmax.top());
			qmax.pop();
		}
		while(qmin.size()>k)
		{
			qmax.push(qmin.top());
			qmin.pop();
		}
		cout<<qmin.top()<<" ";
	}
	return 0; 
} 

P2085 最小函数值

这题不同于之前,算是一种变式吧,你只需要用一个大顶堆来维护前面m个最小的即可,如果i==1则直接存进去,然后再后续如果小于堆顶元素就存进去,反之则要直接结束即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int a,b,c;
int ans[200005];
priority_queue<int> q;//建立大根堆 
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a>>b>>c;
		for(int j=1;j<=m;j++)
		{
			int num=a*j*j+b*j+c;
			if(i==1)//先搞进去m个,控制范围 
			{
				q.push(num);
			}
			else
			{
				if(num<q.top())//有更小的加进去,弹出栈顶 
				{
					q.push(num);
					q.pop();
				}
				else//优化节约时间 
				{
					break;
				}
			}
		}
	}
	for(int i=1;i<=m;i++)
	{
		ans[i]=q.top();
		q.pop();
	}
	for(int i=m;i>=1;i--)
	{
		cout<<ans[i]<<" ";
	 } 
}

 P1631 序列合并

和上面那个一样的思路

#include<bits/stdc++.h>
using namespace std;
#define int long long

int n;
int a[100005];
int b[100005];
int ans[100005];
priority_queue<int> q;

signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	for(int i=1;i<=n;i++)
	{
		cin>>b[i];
	}
	sort(a+1,a+1+n);
	sort(b+1,b+1+n); 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			int num=a[i]+b[j];
			if(i==1)
			{
				q.push(num);
			}
			else
			{
				if(num<q.top())
				{
					q.push(num);
					q.pop();
				}
				else
				break;
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		ans[i]=q.top();
		q.pop();
	}
	for(int i=n;i>=1;i--)
	cout<<ans[i]<<" ";
	return 0;
}

 P1801 黑匣子

 

思路:只不过是去求第k小罢了也是板子,直接做 

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int a[200005];
int b;
int vis[200005];
priority_queue<int> qmax;
priority_queue<int,vector<int>,greater<int>> qmin;
signed main()
{
    cin>>n>>m;
    int k=0;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }
    for(int i=1; i<=m; i++)
    {
        cin>>b;
        vis[b]++;
    }
    for(int i=1; i<=n; i++)
    {
        if(qmax.empty()||a[i]<=qmax.top())
            qmax.push(a[i]);
        else
            qmin.push(a[i]);
        while(vis[i])
        {
            vis[i]--;
            k++;
            while(qmax.size()<k)
            {
                qmax.push(qmin.top());
                qmin.pop();
            }
            while(qmax.size()>k)
            {
                qmin.push(qmax.top());
                qmax.pop();
            }
            cout<<qmax.top()<<"\n";
        }
    }
    return 0;
}

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值