优先级队列

优先级队列的基本使用
模拟实现上面的接口函数,优先级队列不是队列,而是类似一个堆一样的东西,我们先来试试它的接口函数是怎么个样子的。
需要包含的头文件是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地址 如下图:...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
