目录
一.什么是对顶堆
我们都知道,堆是一种及其有用的数据结构,他可以在短时间内维护出区间最值,但普通的堆能起到的作用毕竟是有限的,但我们可以考虑变形,解决更多的问题,例如今天我们要讲的对顶堆。(如果不知道什么是堆的小伙伴,可以参考这篇)
顾名思义,对顶堆是两个堆,一个大根堆,一个小根堆组成的特殊的数据结构
二.对顶堆的性质
如上图,如果说上面是个小根堆,下面是个大根堆,大根堆的元素都小于于小根堆的元素,那么我们可以发现:
每一层节点从上往下逐层递减
三.对顶堆的应用
对顶堆可以被用来解决一系列与最值有关的问题,例如:
- 在一大堆数据中,查找最大或最小值。
- 维护一个有序序列(例如优先级队列)。
- 求中位数或者第 K 大/小的数。
对顶堆的时间复杂度为 O(log n)。
对顶堆本质上是通过不断地调整,而维护大根堆里的所有元素比小根堆小这个性质
举个例子,求前i个数字的中位数题目详情见洛谷P1168 中位数
但我们在输出答案前需要对midmid进行调整,如果小根堆和大根堆内元素相同,就无需处理,此时midmid仍然是当前的中位数。
如果两个堆中元素个数不同,那我们就需要进行调整。
具体是把元素个数较多的堆的堆顶作为mid,mid加入元素较少的堆。
同理,如果q2中元素比q1多,同理
代码如下:
#include <bits/stdc++.h>
using namespace std;
int a[100010],n;
priority_queue<int> q1;
priority_queue<int,vector<int>,greater<int> > q2;
int main() {
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
q2.push(a[1]);
for(int i=2;i<=n+1;i++){
if(i%2==0) cout<<q2.top()<<"\n";
if(i>n) break;
if(q2.size()>q1.size()){
if(a[i]>q2.top()){
q1.push(q2.top());
q2.pop();
q2.push(a[i]);
}else q1.push(a[i]);
}else{
if(a[i]<q1.top()){
q2.push(q1.top());
q1.pop();
q1.push(a[i]);
}else q2.push(a[i]);
}
}
return 0;
}