蓝桥杯备考:堆和priority queue(优先级队列)

堆的定义

heap堆是一种特殊的完全二叉树,对于树中的每个结点,如果该结点的权值大于等于孩子结点的权值,就称它为大根堆,小于等于就叫小根堆,如果是大根堆,每个子树也是符合大根堆的特征的,如果是小根堆,每课子树符合小根堆特征

堆的存储

顺序存储,按照层序遍历编号依次存放在数组里面,变量n负责标记元素个数,存储固然简单,但是我们的 题目一般不会那么好心直接给一个标准的堆,一般只是随便给我们一组数,这组数还原出来并不一定是堆结构,我们可以用数组存下来这组数,然后把数组调整称堆,也可以依次把这些数插入到堆里面

向上调整算法 

向上调整,当我们插入一个数的时候,我们需要进行向上调整

算法原理(大根堆,小根堆同理):如果插入的数权值小于等于父亲结点,那我们不变,如果插入的数是大于父亲结点的,我们就要让它和父亲结点换位置

不断重复这个操作,直到此数变成根结点或者权值小于等于父亲结点的时候为止

比如我们尝试调整一下这棵树

void up(int child)
{int parent = child / 2;while (child != 1 && heap[parent] < heap[child]){swap(heap[parent], heap[child]);child = parent;parent = child / 2;}}

向下调整算法

当我们需要删除一个结点的时候,我们只能删除堆顶的元素,这时候,我们交换堆底和堆顶元素,然后对交换上来的堆顶进行向下调整算法,如果新的堆顶的两个孩子都小于等于它,那么就不用操作,因为我的其他子树都维持着大根堆的特征,如果堆顶元素的两个孩子的较大结点大于我们的堆顶元素,此时我们就要让他和最大的孩子交换,重复此操作,直到换到叶子结点或者该结点大于等于左右孩子为止

void down(int parent)
{int child = parent * 2;if (heap[child + 1] > heap[child]) child = child + 1;while (child <= n && heap[parent] < heap[child]){swap(heap[parent], heap[child]);parent = child;child = parent * 2;}}

由于我们的向上调整算法和向下调整算法最坏的情况都是执行二叉树的深度次,又因为二叉树的深度是log(n+1) 所以我们的向上调整算法和向下调整算法就是O(log N)

堆数据结构的创建

创建

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int heap[N], n;

插入

插入的时候我们就放在n下标的下一个位置,然后把新的n下标传进去进行向上调整即可

代码;

void push(int x)
{heap[++n] = x;up(n);
}

删除

删除的时候我们要先交换堆顶和堆底元素,然后删除堆底元素n--,

然后对堆顶元素进行向下调整

void pop()
{swap(heap[n], heap[1]);n--;down(1);
}

查询堆顶元素

int top()
{return heap[1];
}

时间复杂度是O(1)

查询有效元素个数

int size()
{return n;
}

进行测试,把这个数组插入到堆里面,键堆,依次取出堆顶元素,就是个降序的过程

总代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int heap[N], n;void up(int child)
{int parent = child / 2;while (child != 1 && heap[parent] < heap[child]){swap(heap[parent], heap[child]);child = parent;parent = child / 2;}}
void down(int parent)
{int child = parent * 2;if (heap[child + 1] > heap[child]) child = child + 1;while (child <= n && heap[parent] < heap[child]){swap(heap[parent], heap[child]);parent = child;child = parent * 2;}}
void push(int x)
{heap[++n] = x;up(n);
}
void pop()
{swap(heap[n], heap[1]);n--;down(1);
}int top()
{return heap[1];
}
int size()
{return n;
}
int main()
{int a[10] = { 1, 41, 23, 10, 11, 2, -1, 99, 14, 0 };for (int i = 0; i < 10; i++){push(a[i]);}while (size()){cout << top() << " ";pop();}return 0;
}

priority queue优先级队列的用法(初阶,进阶)

在我们的stl中,有一个现成的堆的数据结构,叫做优先级队列,有了它,我们就不用再自己手写一个堆的数据结构了

堆可以存储内置类型比如说int

堆也可以存储字符串类型

堆也可以存储自定义类型比如结构体

每一种存储方式都对应一种写法

我们先来介绍普通的存储内置类型的优先级队列

#include <iostream>
#include <queue>
using namespace std;int a[10] = { 1, 41, 23, 10, 11, 2, -1, 99, 14, 0 };int main()
{priority_queue <int> heap;//这种简单的写法默认就是大根堆for (int i = 0; i < 10; i++){heap.push(a[i]);}while (heap.size()){cout << heap.top() << " ";heap.pop();}return 0;
}

首先是这种简单的写法,这种简单的写法默认就是大根堆

如果你想实现正经八本的一个堆的话,我们有一个模式

priority_queue<数据类型,数据实现方式,比较方式(判断大/小根堆)>

	priority_queue<int, vector<int>, less<int>> q1;//大根堆
	priority_queue<int, vector<int>, greater<int>> q2;//小根堆

这个需要我们特殊的记一下,大根堆用less(当第一个数小于第二个数是返回true,相当于我们sort里的比较函数一样),小根堆用greater

测试结果

测试代码

#include <iostream>
#include <queue>
using namespace std;int a[10] = { 1, 41, 23, 10, 11, 2, -1, 99, 14, 0 };
void test()
{priority_queue<int, vector<int>, less<int>> q1;//大根堆priority_queue<int, vector<int>, greater<int>> q2;//小根堆for (int i = 0; i < 10; i++){q1.push(a[i]);q2.push(a[i]);}while (q1.size()){cout << q1.top() << " ";q1.pop();}cout << endl;while (q2.size()){cout << q2.top() << " ";q2.pop();}cout << endl;
}
int main()
{priority_queue <int> heap;//这种简单的写法默认就是大根堆/*for (int i = 0; i < 10; i++){heap.push(a[i]);}while (heap.size()){cout << heap.top() << " ";heap.pop();}*/test();return 0;
}

如果我们要存double和long long 只要把int改一下就行了

接下来我们来说一下结构体类型怎么建堆

如果想实现结构体类型的建堆,我们只需要在结构体内部重载小于号,如果返回b<x.b的话,就是大根堆,如果返回b>x,b的话就是小根堆

struct node {int x, y, z;bool operator < (const node& e)const {return y < e.y;//大根堆}
};
void test2()
{priority_queue <node> heap5;for (int i = 1; i <= 5; i++){heap5.push({ i + 5,i + 1,i + 2 });}while (heap5.size()){node t = heap5.top(); heap5.pop();cout << t.x << " " << t.y << " " << t.z << endl;}
}

可以看到这就是大根堆

把重载的返回值改成大于就变成小根堆了,我们就不再放代码了,只要改一个符号就行

堆和priority优先级队列的算法题

1.模板题

这是一道模板题,我们先用自己手写的堆实现一下

#include <iostream>
using namespace std;const int N = 1e6+10;
int heap[N],n;
void up(int child)
{int parent = child/2;while(parent >=1 && heap[parent]>heap[child]){swap(heap[parent],heap[child]);child = parent;parent = child/2;}
}
void down(int parent)
{int child = parent * 2;while(child <= n){if(child + 1 <= n && heap[child+1] < heap[child])++child;if(heap[parent] < heap[child]) return;swap(heap[parent],heap[child]);parent = child;child = parent*2;}
}
void push(int x)
{heap[++n] = x;up(n);
}
void pop()
{swap(heap[1],heap[n]);n--;down(1);
}
int main()
{int T;cin >> T;while(T--){int op;cin >> op;if(op == 1){int t;cin >> t;push(t); }else if(op == 2){cout << heap[1] << endl;}else{pop();}}return 0;
}

接着用我们stl的优先级队列来实现一下

#include <iostream>
#include <queue>
using namespace std;
priority_queue<int,vector<int>,greater<int>> heap1;
int main()
{int n;cin >> n;while(n--){int op;cin >> op;if(op == 1){int x;cin >> x;heap1.push(x);}else if(op == 2)cout << heap1.top() << endl;elseheap1.pop();}return 0;
}

2.第k小(topk问题)

我们要维护这个优先级队列,让优先级队列里只有k个元素,然后堆顶就是我们的第k小,比如1,2,3 3就是第三小,如果要找第k小的话,实际上就是k个元素的时候的最大的值,也就是k个元素的时候的堆顶,比如1 2 3 3就是第k小,如果你插入4 5 6 1 2 3 4 5 6 最小的还是3,所以我们依次删除堆顶,留下 1 2 3 3 当堆顶的时候就是第三小  再比如 1 2  5  插入了3  那 我们删除最大的堆顶 留下 1 2 3 3也就是第三小了

#include <iostream>
#include <queue>
using namespace std;
priority_queue <int> heap1;
const int N = 2e5+10;
int a[N];int main()
{int n,m,k;cin >> n >> m >> k;for(int i = 0;i<n;i++){cin >> a[i];heap1.push(a[i]);}while(heap1.size() > k){int t = heap1.top();heap1.pop();}while(m--){int op;cin >> op;if(op == 1){int x;cin >> x;heap1.push(x);while(heap1.size() > k){heap1.pop();}}if(op == 2){if(heap1.size() < k)cout << -1 << endl;elsecout << heap1.top() << endl;}}}

3.除2!

如果遇到求和,我们一定要记得考虑用long long

#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
int n,k;
ll sum;
priority_queue <int> heap;
int main()
{cin >> n >> k;for(int i = 1;i<=n;i++){int x;cin >> x;sum+=x;if(x%2 == 0){heap.push(x);}}while(heap.size() && k--){int t = heap.top()/2;heap.pop();sum-=t;if(t%2 == 0){heap.push(t);}}cout << sum << endl;return 0;
}

4.最小函数值(结构体堆+单调性)

这道题我们要用构造一个结构体元素的堆,根据单调性,我们不需要一次性把所有的函数式前m个元素算出来,我们只需要先把代入值为1的值算出来,push成一个堆,然后把堆顶打印出来,把这个堆顶元素的代入值变为2再push进去,接下来再打印下一个堆顶即可

所以我们结构体要存下函数值(建堆的条件)还要存第几个函数表达式,并存下代入值x

#include <iostream>
#include <queue>
using namespace std;
const int N = 1e4+10;
int a[N],b[N],c[N];
int n,m;struct node
{int f;int num;int x;bool operator <(const node& y) const{return f > y.f;//前一个元素大于后一个元素 }	
};
int calc(int i,int x)
{return a[i]*x*x + b[i]*x +c[i];
}
priority_queue <node> q;
int main()
{cin >> n >> m;for(int i = 1;i<=n;i++){cin >> a[i] >> b[i] >> c[i];}//先把代入值为1的元素push进去,然后依次出堆顶//每次出堆顶都代入这个表达式代入的下一个值//这个循环一共循环m次 for(int i = 1;i<=n;i++){q.push({calc(i,1),i,1});}while(q.size() && m--){node t = q.top();q.pop();cout << t.f << " ";int x1 = t.x+1; int num1 = t.num;q.push({calc(num1,x1),num1,x1});}return 0;
}

5.序列合并(结构体堆+单调性)

这道题和上一道题非常的类似

我们这么想,先让第一个序列里最小的值和第二个序列分别相加,push进入小根堆,删除堆顶再push进第一个序列第二个小的值和第二个序列那个值相加的值,直到打印完前n个值

#include <iostream>
#include <queue>
using namespace std;
const int N = 1e5+10;
int a[N],b[N];
typedef long long ll;
struct node{ll sum;int i;int j;bool operator <(const node& n) const{return sum > n.sum;}};
priority_queue <node> heap;
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];for(int i = 1;i<=n;i++){heap.push({a[i]+b[1],i,1});}for(int i = 1;i<=n;i++){node t = heap.top(); heap.pop();ll sum1 = t.sum,i1 = t.i,j1=t.j;cout << sum1 << " ";heap.push({a[i1]+b[j1+1],i1,j1+1});}return 0;} 

6.舞蹈课(双链表存数据,结构体元素组成的堆)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/4079.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快速搭建深度学习环境(Linux:miniconda+pytorch+jupyter notebook)

本文基于服务器端环境展开&#xff0c;使用的虚拟终端为Xshell。 miniconda miniconda是Anaconda的轻量版&#xff0c;仅包含Conda和Python&#xff0c;如果只做深度学习&#xff0c;可使用miniconda。 [注]&#xff1a;Anaconda、Conda与Miniconda Conda&#xff1a;创建和管…

springboot医院信管系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

《自动驾驶与机器人中的SLAM技术》ch8:基于预积分和图优化的紧耦合 LIO 系统

和组合导航一样&#xff0c;也可以通过预积分 IMU 因子加上雷达残差来实现基于预积分和图优化的紧耦合 LIO 系统。一些现代的 Lidar SLAM 系统也采用了这种方式。相比滤波器方法来说&#xff0c;预积分因子可以更方便地整合到现有的优化框架中&#xff0c;从开发到实现都更为便…

微信消息群发(定时群发)-UI自动化产品(基于.Net平台+C#)

整理 | 小耕家的喵大仙 出品 | CSDN&#xff08;ID&#xff1a;lichao19897314&#xff09; 关联源码及工具下载https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/…

ESP8266-01S、手机、STM32连接

1、ESP8266-01S的工作原理 1.1、AP和STA ESP8266-01S为WIFI的透传模块&#xff0c;主要模式如下图&#xff1a; 上节说到&#xff0c;我们需要用到AT固件进行局域网应用&#xff08;ESP8266连接的STM32和手机进行连接&#xff09;。 ESP8266为一个WiFi透传模块&#xff0c;和…

动手学大数据-3社区开源实践

目录 数据库概览&#xff1a; MaxComput&#xff1a; HAWQ&#xff1a; Hologres&#xff1a; TiDB&#xff1a; Spark&#xff1a; ClickHouse&#xff1a; Apache Calcite 概览 Calcite RBO HepPlanner 优化规则&#xff08;Rule&#xff09; 内置有100优化规则 …

WPS数据分析000004

目录 一、表格阅读技巧 冻结窗格 拆分窗口 新建窗口 阅读模式 护眼模式 二、表格打印技巧 打印预览 打印缩放 打印区域 打印标题 分页打印 打印位置 页眉页脚 逐份打印 三、表格保护技巧 锁定单元格 隐藏公式 文档权限 文件加密 一、表格阅读技巧 冻结窗…

【Android】蓝牙电话HFP连接源码分析

一、概述 在Android系统中&#xff0c;HF&#xff08;Hands-Free Profile&#xff09;客户端与AG&#xff08;Audio Gateway&#xff09;端之间的HFP&#xff08;Hands-Free Profile&#xff09;连接是蓝牙音频通信的重要组成部分。这一过程涉及多个层次和组件的协同工作&…

Redisson发布订阅学习

介绍 Redisson 的消息订阅功能遵循 Redis 的发布/订阅模式&#xff0c;该模式包括以下几个核心概念&#xff1a; 发布者&#xff08;Publisher&#xff09;&#xff1a;发送消息到特定频道的客户端。在 Redis 中&#xff0c;这通过 PUBLISH 命令实现。 订阅者&#xff08;Sub…

【Linux 重装】Ubuntu 启动盘 U盘无法被识别,如何处理?

背景 U盘烧录了 Ubuntu 系统作为启动盘&#xff0c;再次插入电脑后无法被识别 解决方案&#xff08;Mac 适用&#xff09; &#xff08;1&#xff09;查找 USB&#xff0c;&#xff08;2&#xff09;格式化&#xff08;1&#xff09;在 terminal 中通过 diskutil list 查看是…

【LLM-RL】DeepSeekMath强化对齐之GRPO算法

note 文章目录 note一、GRPOReference 一、GRPO 论文&#xff1a;DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models &#xff08;https://arxiv.org/pdf/2402.03300&#xff09;GRPO 在 DeepSeek V2 中采用了&#xff0c;GRPO 在训练过程…

Rust Actix Web 项目实战教程 mysql redis swagger:构建用户管理系统

Rust Actix Web 项目实战教程&#xff1a;构建用户管理系统 项目概述 本教程将指导你使用 Rust 和 Actix Web 构建一个完整的用户管理系统&#xff0c;包括数据库交互、Redis 缓存和 Swagger UI 文档。 技术栈 Rust 编程语言Actix Web 框架SQLx (MySQL 数据库)Redis 缓存Uto…

git系列之revert回滚

1. Git 使用cherry-pick“摘樱桃” step 1&#xff1a; 本地切到远程分支&#xff0c;对齐要对齐的base分支&#xff0c;举例子 localmap git pull git reset --hard localmap 对应的commit idstep 2&#xff1a; 执行cherry-pick命令 git cherry-pick abc123这样就会将远程…

Hadoop•用Web UI查看Hadoop状态词频统计

听说这里是目录哦 通过Web UI查看Hadoop运行状态&#x1f407;一、关闭防火墙二、在物理计算机添加集群的IP映射三、启动集群四、进入HDFS的Web UI 词频统计&#x1f9a9;1、准备文本数据2、在HDFS创建目录3、上传文件4、查看文件是否上传成功5、运行MapReduce程序6、查看MapRe…

国产编辑器EverEdit -重复行

1 重复行 1.1 应用场景 在代码或文本编辑过程中&#xff0c; 经常需要快速复制当前行&#xff0c;比如&#xff0c;给对象的多个属性进行赋值。传统的做法是&#xff1a;选中行-> 复制-> 插入新行-> 粘贴&#xff0c;该操作有4个步骤&#xff0c;非常繁琐。 那有没…

LabVIEW桥接传感器数据采集与校准程序

该程序设计用于采集来自桥接传感器的数据&#xff0c;执行必要的设置&#xff08;如桥接配置、信号采集参数、时间与触发设置&#xff09;&#xff0c;并进行适当的标定和偏移校正&#xff0c;最终通过图表呈现采集到的数据信息。程序包括多个模块&#xff0c;用于配置通道、触…

redis-排查命中率降低问题

1.命中率降低带来的问题 高并发系统&#xff0c;当命中率低于平常的的运行情况&#xff0c;或者低于70%时&#xff0c;会产生2个影响。 有大量的请求需要查DB&#xff0c;加大DB的压力&#xff1b;影响redis自身的性能 不同的业务场景&#xff0c;阈值不一样&#xff0c;一般…

edge浏览器恢复旧版滚动条

1、地址栏输入edge://flags 2、搜索Fluent scrollbars.&#xff0c;选择disabled&#xff0c;重启即可

【算法】算法基础课模板大全——第一篇

由于本文章内容太长&#xff0c;导致文章不能以一篇博客形式发布出来&#xff0c;所以我将分为两篇博客进行发布。 【算法】算法基础课模板大全——第一篇 【算法】算法基础课模板大全——第二篇 此笔记适用于AcWing网站的算法基础课&#xff0c;所有的资源链接、代码模板全部来…

Top期刊算法!RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测

Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测 目录 Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于RIME-CNN-BiLSTM-Attention、CNN-BiLSTM-Attention、R…