对顶堆介绍
对顶堆是由一个大顶堆和一个小顶堆组合而成的数据结构
用途
与传统堆维护最大数不同,对顶堆用于动态维护第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;
}