先浅谈一下堆
优先队列:
大根堆:priority_queue < int > q
小根堆: priority_queue<int, vector< int>, greater< int>> q
常用函数:
代码 | 含义 |
---|---|
q.top() | 访问队首元素 |
q.push() | 入队 |
q.pop() | 堆顶(队首)出队 |
q.size() | 队列元素个数 |
q.empty() | 是否为空 |
注意:没有clear(),优先队列只能通过top()来访问元素 | 无法一次性删除队列 |
经典例题:动态中位数
依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。
**解题思路:**可以用对顶堆。维护一个最大堆一个最小堆,最大堆里存的是较小的一半数据,最小堆里存的是较大的一半数据,并且维持最大堆元素个数不少于最小堆元素个数,并且最大堆元素个数与最小堆元素个数之差小于等于1。这样当输入了奇数个数的时候,最大堆的堆顶就是中位数。每次来一个数的时候,先决定其入哪个堆,然后如果堆的元素个数出现了违法上面规定的情况,就做相应的调整。
代码如下:
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
//priority_queue<int> maxheap;
//priority_queue<int,vector<int>,greater<int>> minheap;
vector<int> v;
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;
cin >> t;
while(t--){
priority_queue<int> maxheap;//每次循环都重新定义最大堆和最小堆
priority_queue<int,vector<int>,greater<int>> minheap;
v.clear();//记得清空数组
int n,m;
cin >> n >> m;
for(int i=1;i<=m;++i){
int x;
cin >> x;
if(maxheap.empty()||maxheap.top()>=x) maxheap.push(x);//当最大堆没元素或者输出的数小于等于最大堆的堆顶元素
else minheap.push(x);//反之存入最小堆
if(minheap.size()>maxheap.size()){//必须保证最大堆元素的个数大于等于最小堆元素个数
x=minheap.top();minheap.pop();
maxheap.push(x);
}
else if(minheap.size()+2<=maxheap.size()){//最小堆与最大堆元素之差最多为1,超过则需调整
x=maxheap.top();maxheap.pop();
minheap.push(x);
}
if(i % 2) v.push_back(maxheap.top());//若为奇数则将最大堆的堆顶存入数组
}
cout << n << " " << v.size() << endl;
for(int i=0;i<v.size();++i){
cout << v[i] << " ";
if(i % 10 == 9 && i+1<v.size()) cout << endl;
}
cout << endl;
}
return 0;
}
做题常用的位运算
位运算
C++ 提供了按位与(&)、按位或(| )、按位异或(^)、取反(~)、左移(<<)、右移(>>)这 6 种位运算符。
& | 按位与 |
---|---|
| | 按位或 |
^ | 按位异或 |
~ | 取反 |
<< | 左移 |
>> | 右移 |
常用的位运算符
按位与运算符(&)
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; 即:两位同时为“1”,结果才为“1”,否则为0
例如:3&5 即 0000 0011& 0000 0101 = 0000 0001 ,结果为1
如果是偶数的话,二进制下最后一个数必然不可能是1,所以&可以用来判断奇偶数
按位左移运算符(<<)和 按位右移运算符(>>)
左移1相当于该数乘以2,左移2相当于该数乘以2*2即乘以4
右移同理,右移1相当于该数除以2,右移2相当于该数除以4
例如,142 << 1=284,142 << 2 = 568,142 >> 1 = 71,142 >> 2 =35。