电子学会七级-数据结构-堆和优先队列
堆的基本存储
https://www.runoob.com/data-structures/heap-storage.html
堆的 shift up
https://www.runoob.com/data-structures/heap-shift-up.html
堆的 shift down
https://www.runoob.com/data-structures/heap-shift-down.html
基础堆排序
https://www.runoob.com/data-structures/heap-sort.html
堆排序
https://www.bilibili.com/video/BV1Eb41147dK?spm_id_from=333.337.search-card.all.click
实现堆
https://www.bilibili.com/video/BV1V44y1j7bJ?spm_id_from=333.337.search-card.all.click&vd_source=29bdf11ae30b7aaca85ec74dc1b8e1ad
【模板】堆
https://www.luogu.com.cn/problem/P3378
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 1e6 + 10;
int n, heap[MAXN], len;
//删除一个节点后 调整最后一个节点到根节点 向下重新构建堆
void shiftDown(int heap[], int start, int end){
// parent为欲调整结点, child 为其左孩子 child+1右孩子
int parent = start;
int child = parent * 2;
while (child <= end) { // 存在孩子节点
if (child + 1 <= end && heap[child] > heap[child + 1])//如果右孩子小 调整右孩子
child++;
if (heap[parent] > heap[child]) {//parent>child 需要把小的调整到父节点 小根堆
swap(heap[parent], heap[child]);// parent child对换
parent = child;//有影响的子节点 设置parent 继续向下调整
child = parent * 2;//继续找左孩子
} else//不需要调整
return;
}
}
//在最后追加一个节点,向上重新构建有影响的堆 对应的节点
void shiftUp(int heap[], int start, int end){
int child = end, parent = child / 2;//child=end 从最后开始调整 找到child 对应 parent
while(parent >= start) {//parent 在需要调整范围内
if(heap[parent] > heap[child]) {//parent > child 调整 小根堆
swap(heap[parent], heap[child]);//对换child 和parent 另一孩子和父节点数据不变 不需要管另一的孩子
child = parent;//child 变成parent 继续向上调整
parent = child / 2;//找父节点
} else {//不需要调整
return;
}
}
}
void insert(int heap[], int x){//数组最后插入一个元素后 向上调整有影响的节点
len++;
heap[len] = x;//让元素个数+1,然后将数组末尾赋为x
shiftUp(heap, 1, len);//调整有影响的节点
}
void del(int heap[]){//删除根节点后 把最后一个元素替换到根节点 向下调整有影响的节点
swap(heap[1], heap[len]);//最后一个元素替换第一个元素-根
len--;//数组数量-1
shiftDown(heap, 1, len);//向下调整有影响的节点
}
int main(){
scanf("%d", &n);
int op, x;
for(int i = 1; i <= n; i++) {
scanf("%d", &op);
if(op == 1){
scanf("%d", &x);
insert(heap, x);
} else if (op == 2) {
printf("%d\n", heap[1]);
} else if(op == 3) {
del(heap);
}
}
return 0;
}
【模板】快速排序
https://www.luogu.com.cn/problem/P1177
合并果子
https://www.luogu.com.cn/problem/P1090
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
priority_queue <ll, vector<ll>, greater<ll> > p;
int n;
cin >> n;
ll ans = 0;
while (n--)
{
ll a;
cin >> a;
p.push(a);
}
while (p.size() >= 2)
{
ll temp = 0;
ll a;
a = p.top();
ans += a;
temp += a;
p.pop();
a = p.top();
ans += a;
temp += a;
p.pop();
p.push(temp);
}
cout << ans << endl;
return 0;
}
瑞瑞的木板
https://www.luogu.com.cn/problem/P1334
//可以逆向思维 假设已经切好 每次选最小的合并 类似合并果子
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll ans;
priority_queue<ll,vector<ll>,greater<ll> > q;//优先队列 小根堆
int main(){
cin>>n;
for(int i=0;i<n;i++){
int num;
cin>>num;
q.push(num);//所有放入优先队列
}
while(q.size()>=2){//只有队列里面有两个 则出队
ll a=q.top();//获取一个 最小
q.pop();
ll b=q.top();//再获取一个 第二小
q.pop();
ans+=a+b;// 最小 + 第二小
q.push(a+b);//和放回队列 参与下次计算
}
cout<<ans;//输出则是切分需要最少能量
}
序列合并
https://www.luogu.com.cn/problem/P1631
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int N,A[maxn],B[maxn],from[maxn];
priority_queue< pair<int,int>,vector< pair<int,int> >,greater< pair<int,int> > > q;
int main(){
cin>>N;
for(int i=1;i<=N;i++){
cin>>A[i];
}
for(int i=1;i<=N;i++){
cin>>B[i];
from[i]=1;//A[i]先与B[1]相加 记录A[i]和 加到B的第几个
q.push(pair<int,int>(A[i]+B[from[i]],i));//相加和 A的下标
}
while(N--){
cout<<q.top().first<<" ";
int pos=q.top().second;
q.pop();
from[pos]++;//A[pos]对应B的下一个 放入队列 参与比较
q.push(pair<int,int>(A[pos]+B[from[pos]],pos));
}
}
【深基16.例1】淘汰赛
https://www.luogu.com.cn/problem/P4715
#include <bits/stdc++.h>
using namespace std;
const int N = 7;
int n;
struct Node {
int country, power;
} t[2 << N + 10];
void build() { // build the tree 两个队产生一个胜利对继续比赛
for (int i = n - 1; i > 0; --i) {
int left = i << 1, right = i << 1 | 1;//left 左子树 right右子树 i<<1|1 =i*2+1
if(t[left].power < t[right].power){
t[i].power = t[right].power;
t[i].country = t[right].country;
} else {
t[i].power = t[left].power;
t[i].country = t[left].country;
}
}
}
int main() {
scanf("%d", &n);
n = 1 << n;
for (int i = 0; i < n; ++i) {
scanf("%d", &t[n+i].power);//读入能力值
t[n+i].country = i + 1;//给国家编号 从1开始
}
build();//构建比赛树
if(t[1].power == t[2].power)// 每个国家能力值不同 且 1==1 说明 1和2是一个国家
printf("%d\n", t[3].country);//亚军是第三个国家
else
printf("%d\n", t[2].country);//1 2不是一个国家 亚军是2
return 0;
}
中位数
https://www.luogu.com.cn/problem/P1168
舞蹈课
https://www.luogu.com.cn/problem/P1878
#include<bits/stdc++.h>
using namespace std;
const int maxn=2*1e5+5;
struct node{
int x,y,z;//两位舞者 x y 技术差 z
node(int xx,int yy,int zz){//构造函数
x=xx;
y=yy;
z=zz;
}
// node2为堆顶元素 node1>node2为小顶堆
//https://blog.csdn.net/qq_44144025/article/details/119355668
friend bool operator < (node node1,node node2){//构造小顶堆
if(node1.z!=node2.z){// 技术差不同 按技术差构造小顶堆
return node1.z>node2.z;
}
return node1.x>node2.x;//技术差相同 按第一个编号构造小顶堆
}
};
priority_queue<node,vector<node>> pqueue;//使用vector构造小顶堆
string s;
int n,q[maxn],idx;//n个舞蹈者 q[]技术值 idx累加ans数组下标
bool f[maxn],vis[maxn];//f[] 男女 true 男 false女 vis是否已跳过舞
pair<int,int> ans[maxn]; //结对跳舞
int main(){
cin>>n;//跳舞人数
cin>>s;//男女字符串
for(int i=0;i<n;i++){//使用f数组把男女分开
if(s[i]=='B'){
f[i+1]=true;
}else{
f[i+1]=false;
}
}
for(int i=1;i<=n;i++){//输入技术值
cin>>q[i];
}
for(int i=1;i<n;i++){
if(f[i]!=f[i+1]){//异性 相邻是异性 放入队列 等待尝试跳舞
pqueue.push(node(i,i+1,abs(q[i]-q[i+1])));
}
}
while(!pqueue.empty()){//队列不为空 处理跳舞顺序
int x = pqueue.top().x;
int y = pqueue.top().y;
pqueue.pop();//取出技术值差小的 一对舞者 x y
if(!vis[x] && !vis[y]){//没跳过 可以去跳
vis[x]=true;//标识x已经跳过,后续不再跳
vis[y]=true;//标识y已经跳过,后续不再跳
ans[++idx].first=x;
ans[idx].second=y;//跳过的x y记录到ans数组
while(x>0 && vis[x]){//当前这一对 前一个和后一个是否可以组合一对舞者
x--;
}
while(y<n && vis[y]){//后一个没有跳过的
y++;
}
if(x > 0 && y <= n && f[x]!=f[y]){//如果是异性 可以组合一对舞者 放入队列尝试去跳
pqueue.push(node(x,y,abs(q[x]-q[y])));
}
}
}
cout<<idx<<endl;//跳舞对数
for(int i=1;i<=idx;i++){//每一对两个成员
cout<<ans[i].first<<" "<<ans[i].second<<endl;
}
return 0;
}
[NOI2015] 荷马史诗
https://www.luogu.com.cn/problem/P2168
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI;//pair两个数组合成一个数
const int N = 1e5+10;
int n, m;
//使用vector存储 PLI的小顶堆
//pair构造的优先队列 默认按第一个树排序 如果第一个相等按第二个树排序
priority_queue<PLI,vector<PLI>,greater<PLI>> heap;
int main(){
cin>>n>>m;//n种单词 使用m进制
for (int i=0;i<n;i++){
LL w;
cin>>w;//每种单词出现次数
heap.push({w, 0});
}
//每次合并一层k-1个数 即: k个数变成1个数 合并了k-1个数
//如果最后恰好为k,则可以合并 因此如果n不够 则最后一层补0
while ((n - 1) % (m - 1)) {
heap.push({0ll, 0});
n++;
}
LL res = 0;
while (heap.size() > 1) {//优先队列不为空 则合并处理
LL sum = 0;//累加本层总数
int depth = 0;//k叉树深度
for (int i = 0; i < m; i++) {//从底向上合并
sum += heap.top().first;//取当前出现次数
depth = max(depth, heap.top().second);//获取高度 赋值depth
heap.pop();//弹出
}
res += sum;//累加编码长度
heap.push({sum, depth + 1});//合并后深度加1
}
cout << res << endl;//编码最短长度
cout << heap.top().second << endl;//最深叶子节点的深度
return 0;
}
黑匣子
https://www.luogu.com.cn/problem/P1801
[HNOI2003]操作系统
https://www.luogu.com.cn/problem/P2278
偏数学
最小函数值
https://www.luogu.com.cn/problem/P2085
作者:newcode 更多资源请关注纽扣编程微信公众号
从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习