优先级队列

优先级队列的基本使用
模拟实现上面的接口函数,优先级队列不是队列,而是类似一个堆一样的东西,我们先来试试它的接口函数是怎么个样子的。
需要包含的头文件是queue。

#include<iostream>
#include<queue>
using namespace std;
void priority_queuetest1()
{priority_queue<int> pq;pq.push(2);pq.push(1);pq.push(4);pq.push(3);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}}
int main()
{priority_queuetest1();return 0;
}
这就是我们的基本使用方法,但是我们运行我们的代码之后发现的打印结果不是升序的
打印结果· : 4 3 2 1
这里就要说明一个问题,我们建立的堆是大堆,也就导致了最后的打印结果就是降序,这个地方会出现一个叫仿函数的东西,我们在后面进行模拟实现的时候回来看到这个。

需要记住小于是大堆,大于是小堆!!!!(后面会解释)
我们的sort排序也是这个样子的。
如果我们用sort对我们的排序要进行排序的时候,默认是升序,原因就是我们进行传仿函数的时候传的是less,我们可以来看看sort的文档。
这里我们传的就是一个对象,而上面的优先级队列传的不是对象,而是模板参数,这里需要注意一下,我们假如要排一个 vector的升序。代码如下。
vector<int> v = { 6,5,4,3,2,1 };sort(v.begin(), v.end());for (auto e : v){cout << e << " ";}
结果 : 1 2 3 4 5 6
如果要排降序的话就需要传一个参数就是仿函数过去。准确来说是对象,而我们上面的优先级队列是类型,编译器会自动去推算!!!!
使用如下
vector<int> v = { 6,5,4,3,2,1 };sort(v.begin(), v.end(),greater<int>());for (auto e : v){cout << e << " ";}
6 5 4 3 2 1
那如果我们要进行的是小堆呢,就可以这样使用。
void priority_queuetest1()
{priority_queue<int,vector<int>,greater<int>> pq;pq.push(2);pq.push(1);pq.push(4);pq.push(3);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}/*vector<int> v = { 6,5,4,3,2,1 };sort(v.begin(), v.end(),greater<int>());for (auto e : v){cout << e << " ";}*/}
这里C语言和C++的区别 ,C语言是没有仿函数的,他是用函数指针来进行调用的。比如qsort函数就是这样的。
仿函数
1 :仿函数其实就是一个类,只要我们在类里面重载了运算符括号,这个类我们就可以认定为具有仿函数功能!
2 : 它的对象可以像函数一样去调用。
3 : 写成模板的时候,那么我们任意数据都可以来进行使用。
那如果我们要写一个最基本的仿函数是怎么个样子,首先就是它得是一个类,是不是要写成模板,取决于你自己。
void priority_queuetest2()
{struct Less//区别于库里面{bool operator()(const int& x, const int& y){return x < y;}};Less com;cout << Less()(1, 2) << endl;cout << com(5, 2) << endl;cout << com.operator()(3,1) << endl;
}
模拟实现队列
我们先实现一个不加上仿函数的优先级队列,根据文档里的接口先来实现一些push, 我们的堆是怎么进行push的呢??
因为我们的堆其实就是一个满二叉树的结构,那在数组里其实就是尾插进去一个数,但是尾插进去之后,可能就不是满二叉树了,所以要做的就是向上调整。
优先级队列的插入push
void push(const T& val){_con.push_back(val);adjust_up(_con.size() - 1);}
push的之后就需要进行向上调整,在这里我们是用了其他容器来进行实现的,因为二叉树是共和数组,所以这里的默认容器就是vector。


需要进行的是向上调整,在我的文章堆里面是讲过这个的,大家可以跳转到下面的这个链接当中
https://blog.csdn.net/2301_76895050/article/details/134611394?spm=1001.2014.3001.5501
向上调整
void adjust_up(size_t child){size_t parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}
还有就是pop,我们需要注意的是pop不是pop尾部的数据,而是数组下标是0位置二的,我们只需要先交换头和尾位置的数据,然后再进行删除,这个时候把头部的数据删掉了,但是却不是二叉树,这个时候(默认是大堆)堆顶的数据不是最大的,所以需要做的就是向下进行调整。
优先级队列的pop
void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}
向下调整
void adjust_down(size_t parent){size_t child = 2 * parent + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1]){child++;}if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}else{break;}}}
其他接口函数都是对我们的容器进行一个封装,所以大体框架就是下面的这个
template<class T,class Container = vector<T>>class priority_queue{public://默认是大堆void adjust_up(size_t child){size_t parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){size_t child = 2 * parent + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1]){child++;}if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}else{break;}}}void push(const T& val){_con.push_back(val);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}bool empty(){return _con.empty();}size_t size(){return _con.size();}T top(){return _con[0];}private:Container _con;};
写完一个代码就需要进行测试一下,我们可以插入一下数据,看看是不是大堆,然后打印我们的数据,来看看是不是有序的就行了、
测试代码
void priority_queuetest1(){priority_queue<int> pq;pq.push(5);pq.push(2);pq.push(1);pq.push(4);pq.push(3);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}}
检查我们的代码的时候其实是可以先来看插入的逻辑的,看看插入之后是不是建立成大堆,如果建立堆问题出错了,就只要两个地方会有问题,一个就是我们插入逻辑,插入逻辑一般不会有问题的,可能再传参数的地方有问题,还有就是我们的向上调整是不是存在一些逻辑问题。
重新认识仿函数
如何控制比较逻辑呢,我们这里默认建立的是大堆,我们需要通过仿函数来进行更改比较逻辑,仿函数最大逻辑就是,对象可以像函数那样进行比较。
快速实现仿函数。
template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};
因为是类,我们可以传递模板参数。所以先来改变优先级队列的参数,要在加上一个仿函数的类模板
所以我们只是需要在我们的函数里面定义一个对象。
template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T,class Container = vector<T>,class Compare = less<T>>class priority_queue{public://默认是大堆void adjust_up(size_t child){Compare com;size_t parent = (child - 1) / 2;while (child > 0){if (com(_con[parent] , _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){Compare com;size_t child = 2 * parent + 1;while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child] , _con[child + 1])){child++;}if (com(_con[parent] , _con[child])){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}else{break;}}}void push(const T& val){_con.push_back(val);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}bool empty(){return _con.empty();}size_t size(){return _con.size();}T top(){return _con[0];}private:Container _con;};void priority_queuetest1(){priority_queue<int> pq;pq.push(5);pq.push(2);pq.push(1);pq.push(4);pq.push(3);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}}
优势1 : 只要更改仿函数就能更改我们的比较逻辑 大堆也就变成小堆
优势2 : 是一个类,理解起来和写起来都比较方便,c语言要实现这样只能通过函数指针,函数指针是个比较麻烦的东西
完整代码
namespace tjl
{template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T,class Container = vector<T>,class Compare = less<T>>class priority_queue{public://默认是大堆void adjust_up(size_t child){Compare com;size_t parent = (child - 1) / 2;while (child > 0){if (com(_con[parent] , _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){Compare com;size_t child = 2 * parent + 1;while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child] , _con[child + 1])){child++;}if (com(_con[parent] , _con[child])){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}else{break;}}}void push(const T& val){_con.push_back(val);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}bool empty(){return _con.empty();}size_t size(){return _con.size();}T top(){return _con[0];}private:Container _con;};void priority_queuetest1(){priority_queue<int,vector<int>,greater<int>> pq;pq.push(5);pq.push(2);pq.push(1);pq.push(4);pq.push(3);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}}
}
补充
仿函数的作用可不只是这样嗷,我们如果要来比较一下我们的日期应该是怎么来实现呢。
class Date{public:friend ostream& operator<<(ostream& _cout, const Date& d);Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}private:int _year;int _month;int _day;};ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
这个时候因为我们假设比较Date,当然我们日期也可以使用之前的方式来进行比较,这是这个时候的比较类型是自定义的。
![]()
代码如下
void priority_queuetest2(){priority_queue<Date> pq;pq.push({2023,1,3});pq.push({2013,1,7});pq.push({2034,3,1});while (!pq.empty()){cout << pq.top() << " ";pq.pop();}}
但是如果我们现在要进行的是Date*的比较呢,这个时候就不可了,首先比较的时候重载必须是自定义的类型,内置类型是不能进行重载的,比较逻辑就是不正确的,所以这个时候要在写一个仿函数
class GreaterPDate{public:bool operator()(const Date* p1, const Date* p2){return *p1 > *p2;}};
以上就是今天的分享,我们下篇文章再见!!!
相关文章:
优先级队列
优先级队列的基本使用 模拟实现上面的接口函数,优先级队列不是队列,而是类似一个堆一样的东西,我们先来试试它的接口函数是怎么个样子的。 需要包含的头文件是queue。 #include<iostream> #include<queue> using namespace std;…...
gitlab使用
个人笔记(整理不易,有帮助,收藏点赞评论,爱你们!!!你的支持是我写作的动力) 笔记目录:学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔…...
ppt技巧:如何将Word文档大纲中导入到幻灯片中?
在PowerPoint中,将Word文档的大纲导入到新的幻灯片是一种非常实用的技巧。以下是详细的步骤: 首先,需要打开PowerPoint软件并打开原始的幻灯片文件。 在PowerPoint的顶部【开始】菜单栏中,找到并点击“新建幻灯片”按钮࿰…...
0.开篇:SSM+Spring Boot导学
1. 为什么要使用框架 Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。 几乎当下所有企业级JavaEE开发都离不开SSM(Spring SpringMVC MyBatis)Spring B…...
7、configMap
1、configMap是什么 类似与pod的配置中心,不会因为pod的创建销毁,相关配置发生改变 pod定义硬编码意味着需要有效区分⽣产环境与开发过程中的pod 定义。为了能在多个环境下复⽤pod的定义,需要将配置从pod定义描 述中解耦出来。 2、向容器中…...
【Java面试题】JVM(26道)
文章目录 JVM面试题基础1.什么是JVM?2.JVM的组织架构? 内存管理3.JVM的内存区域是什么?3.1堆3.2方法区3.3程序计数器3.4Java虚拟机栈3.5本地方法栈 4.堆和栈的区别是什么?5.JDK1.6、1.7、1.8内存区域的变化?6.内存泄露…...
(十三)强缓存和协商缓存的区别
一、浏览器的缓存策略 浏览器的缓存策略是指浏览器在加载页面时如何使用和管理缓存机制。可以提高网页加载速度,减轻服务器负载,并提供更好的用户体验。常用的缓存策略有两种:一种是发送请求(协商缓存),一…...
如何创建Windows下google Chrome便携版?
创建google Chrome便携版教程 准备工作: 1,下载GoogleChromePortable启动器 2,下载谷歌浏览器 3,下载7-ZIP 解压提取器 用7zip解压GoogleChromePortable,得到GoogleChromePortable.exe启动器 解压谷歌浏览器 用7…...
rabbitmq安装rabbitmq-delayed-message-exchange插件
下载地址:Community Plugins | RabbitMQ 上传到rabbitmq安装目录的/plugins目录下 我的是/usr/lcoal/rabbitmq/plugins/ 直接安装 [rootk8s-node1 rabbitmq]# rabbitmq-plugins enable rabbitmq_delayed_message_exchange [rootk8s-node1 rabbitmq]# rabbitmq-pl…...
B02、分析GC日志-6.3
1、相关GC日志参数 -verbose:gc 输出gc日志信息,默认输出到标准输出-XX:PrintGC 输出GC日志。类似:-verbose:gc-XX:PrintGCDetails 在发生垃圾回收时打印内存回收详细的日志, 并在进程退出时输出当前内存各区域分配情况-XX:PrintGCTimeStamp…...
Redis中的集群(二)
节点 集群数据结构 redisClient结构和clusterLink结构的相同和不同之处 redisClient结构和clusterLink结构都有自己的套接字描述符和输入、输出缓冲区,这两个结构的区别在于,redisClient结构中的套接字和缓冲区是用于连接客户端的,而clust…...
UVA12538 Version Controlled IDE 题解 crope
Version Controlled IDE 传送门 题面翻译 维护一种数据结构,资磁三种操作。 1.在p位置插入一个字符串s 2.从p位置开始删除长度为c的字符串 3.输出第v个历史版本中从p位置开始的长度为c的字符串 1 ≤ n ≤ 50000 1 \leq n \leq 50000 1≤n≤50000,所…...
OAuth2.0客户端和服务端Java实现
oauth2 引言 读了《设计模式之美》和《凤凰架构》架构安全篇之后,决定写一个OAuth2.0的认证流程的Demo,也算是一个阶段性的总结,具体原理实现见《凤凰架构》(架构安全设计篇)。 涉及到的源码可以从https://github.com/WeiXiao-Hyy/oauth2获…...
物流自动分拣系统激光雷达漫反射板
早在二十世纪六十年代,激光器的诞生为激光雷达技术的发展奠定了基础。随后,激光雷达技术开始应用于各种领域,包括军事、航空、地理勘测等。然而,在物流自动分拣领域,激光雷达的应用相对较晚。 随着物流行业的快速发展和…...
2024 抖音欢笑中国年(三):编辑器技巧与实践
前言 本次春节活动中,我们大部分场景使用内部的 SAR Creator互动方案来实现。 SAR Creator 是一款基于 TypeScript 的高性能、轻量化的互动解决方案,目前支持了Web和字节内部跨端框架平台,服务于字节内部的各种互动业务,包括但不限…...
Python学习入门(1)——基础语句(二)
14. 迭代器和迭代协议 在Python中,迭代器是支持迭代操作的对象,即它们可以一次返回其成员中的一个。任何实现了 __iter__() 和 __next__() 方法的对象都是迭代器。 class Count:def __init__(self, low, high):self.current lowself.high highdef __i…...
vue 百度地图 使用 vue-baidu-map 进行当前位置定位和范围展示
vue 百度地图 使用 vue-baidu-map 进行当前位置定位和范围展示(考勤打卡) 一、创建百度地图账号,获取秘钥二、 引入插件1、安装vue-baidu-map2、在main.js中引入 三、 简单使用 最近写项目的时候,做到了考勤打卡的模块内容&#x…...
使用idea运行程序,发现控制台的中文出现乱码
修改UTF-8发现没有效果,寻找.idea文件夹的encodings.xml文件,将里面的UTF-8全部变成GBK....
基于javassm实现的大学生兼职信息系统
开发语言:Java 框架:ssm JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7(一定要5.7版本) 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包&…...
O2OA开发平台如何查看数据表结构?
在访问后端api地址,页面最下方有列示平台的各个服务,点击进入可查看具体的表内容 后端api地址: http://{hostIP}/x_program_center/jest/list.html 其中:{hostIP}为中心服务器所在域名或者IP地址 如下图:...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
