堆
堆是一个完全二叉树实现的,主要分为大根堆和小根堆,只有根节点有意义
插入(up):o(log n)
删除(down):o(log n) 删除根节点
大根堆
例子:
子节点比父亲节点小,根节点最大
9
/ \
6 8
/ \ / \
5 3 7 0
/ \ /
4 1 2
插入:
在最后一行第一个空余的位置插入,如果父亲节点比这个插入的节点小,就进行交换,
如果满足大根堆得条件或到达堆顶,就停止操作
删除:
删除根节点,将最后一从左往右数最后一个元素移动到根节点上,如果此时满足大根堆得特点,就说此时是一个大根堆,如果不满足,就将它的两个子节点中较大的哪个子节点进行交换,重复上述操作,若满足大根堆得特点,就停止操作
小根堆
子节点比父亲节点大,根节点最小
例子:
1
/ \
2 3
/ \ / \
4 5 6 7
插入:
在最后一行第一个空余的位置插入,如果父亲节点比这个插入的节点大,就进行交换,
如果满足小根堆得条件或到达堆顶,就停止操作
删除:
删除根节点,将最后一从左往右数最后一个元素移动到根节点上,如果此时满足小根堆的特点,就说此时是一个小根堆,如果不满足,就将它的两个子节点中小的那个子节点进行交换,重复上述操作,若满足小根堆得特点,就停止操作
优先队列
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出的行为特征。
优先队列底层是堆实现的,也分为大根堆和小根堆
降序优先队列:priority_queue<int> q; 大根堆
升序优先对列priority_queue<int,vector<int>,greater<int> > q; 小根堆
q.push(x) 入队
q.top() 返回队首元素
q.pop() 队首元素出队
q.empty() 判断是否为空
q.size() 获取大小
重载运算符
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,简化操作 让已有的运算符 适应不同的数据类型,通常用于结构体
例子:
bool operator<(const node&b) const {
return val>b.val;
}
构造函数
对结构体进行赋值
例子:
struct node{
int x,y;
node(int a,int b,int c){
x=a;y=b;val=c;
}
};
例题
序列合并
题目描述
有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到N^2个和,求这个N^2和中最小的N个。
输入描述
第一行一个正整数N;
第二行N个整数Ai
第三行N个整数Bi
输出描述
输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。
输入样例
3
2 6 6
1 4 8
输出样例
3 6 7
思路一
创造一个结构体,分为三个值,x为a数组的下标,y为b数组的下标,val为a[i]+b[j]的和,将a数组和b数组从小到大排序
创立一个大根堆,将{1,1,a[1]+b[1]}放入队列,每次输出最小的(此时a[1]+b[1]肯定是最小的,因为排序过).
建立一个map去重,进行n次循环如果还没有被标记过,就将{x+1,y,a[x+1]+b[y]}或{x,y+1,a[x],b[y+1]}放入队列
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e7+10;
int a[maxn],b[maxn];
struct node{
int x,y;
long long val;
bool operator<(const node&b) const {
return val>b.val;
}
node(int a,int b,int c){
x=a;y=b;val=c;
}
};
map<pair<int,int>,int> mp;
priority_queue<node> q;
int main(){
int n;
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+n+1);
sort(b+1,b+n+1);
q.push({1,1,a[1]+b[1]});
while(n--){
int x=q.top().x,y=q.top().y;
cout<<q.top().val<<" ";
q.pop();
if(mp[{x+1,y}]==0){
q.push({x+1,y,a[x+1]+b[y]});
mp[{x+1,y}]=1;
}
if(mp[{x,y+1}]==0){
q.push({x,y+1,a[x]+b[y+1]});
mp[{x,y+1}]=1;
}
}
return 0;
}
思路二
时间复杂度O(n^2)
先枚举n^2个两个数的和,排序后,创造一个大根堆,若大根堆中的元素个数小与n,
将a[i]+b[j]的值入队,如果不满足,a[i]+b[j]如果小与队首元素,将队首元素出队,
并将a[i]+b[j]入队,有如果不小于,直接退出循环,因为此时已经排序过。
#include <bits/stdc++.h>
using namespace std;
priority_queue<long long> q;
const int maxn=1e7+10;
int a[maxn],b[maxn],c[maxn];
int main(){
int n;
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+n+1);
sort(b+1,b+n+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x=a[i]+b[j];
if(q.size()<n) q.push(x);
else{
if(x<q.top()){
q.pop();
q.push(x);
}
else break;
}
}
}
int k=n,o=0;
while(k--){
c[++o]=q.top();
q.pop();
}
for(int i=o;i>=1;i--){
cout<<c[i]<<" ";
}
return 0;
}