当前位置: 首页 > news >正文

C++——优先级队列(priority_queue)的使用及实现

目录

一.priority_queue的使用

1.1、基本介绍

1.2、优先级队列的定义

1.3、基本操作(常见接口的使用)

1.4、重写仿函数支持自定义数据类型

二.priority_queue的模拟实现

2.1、构造&&重要的调整算法

2.2、常见接口的实现

push()

pop()

top()

empty()、size()

 三.利用仿函数改进调整算法


一.priority_queue的使用

1.1、基本介绍

我们之前讲过数据结构中的队列,它具有先进先出的特性(FIFO).添加元素时只能在队尾插入,删除元素时只能删除队首的元素.

优先级队列,它并不满足先进先出的特性,倒像是数据结构中的“堆”. 优先级队列每次出队时只能是队列中优先级最高的元素而不是队首的元素。

这个优先级可以通过元素的大小,或者赋值运算符重载等进行比较. 例如定义元素越大,优先级越高,那么每次出队的时候一定是队列中最大的元素,因为它的优先级最高.并且重新进行维护.

经过上述的说明,是不是和我们所说的“堆”很相似,优先级队列的内部确实是由堆结构实现.

下面是官方文档的一段介绍:

1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。

1.2、优先级队列的定义

首先,使用优先级队列,需要包含头文件<queue>,priority_queue的定义如下:

template <class T, class Container = vector<T>,  class Compare = less<typename Container::value_type> > class priority_queue;

第一个模板参数为为class T,代表每个元素的类型.

第二个模板参数为class Container,缺省值为vector<T>,代表存储这些数据的容器,可以是vector,deque等,但不能是list,因为它的内部空间不连续.

第三个模板参数为class Compare,缺省值为less<T>,其中less是个仿函数,是降序排序,既优先级最大的是容器中最大的元素.又叫比较函数.

当然可以升序排序,把less改为greater即可.

less 和 greater使用的前提是建立在这些数据类型是C++基本的数据类型.

例如下面这段代码:

	//不写后面两个参数默认为vector,lesspriority_queue<int> pq1;//建立一个优先级队列(大堆),数据类型是int,利用vector容器实现,less(降序)实现priority_queue<int, vector<int>, less<int>> pq2;//建立一个优先级队列(小堆),数据类型是int,利用vector容器实现,greater(降序)实现priority_queue<int, vector<int>, greater<int>> pq3;

1.3、基本操作(常见接口的使用)

它的操作与基本队列操作一样,主要有以下接口:

top()  :返回元素中第一个元素的引用(优先级最高的元素都会被放到顶部,既第一个元素).

push():插入一个元素,并重新维护堆,无返回值.

pop() 删除优先级最高的元素,并重新维护堆无返回值

size() :返回容器中有效元素的数量,返回队列的大小

empty() :检测容器是否为空.返回“true”或者“false”.

代码示例:

int main()
{//不写后面两个参数默认为vector,lesspriority_queue<int> pq1;//push的使用pq1.push(1);pq1.push(2);pq1.push(3);pq1.push(4);pq1.push(5);//push完之后,维护也完毕,此时优先级最高的是元素是5,排在第一位cout << pq1.top() << endl;//优先级最高的一位,所以应该是5//pop的使用:删除一个优先级最高的元素5,此时重新调整,优先级最高的元素应该为4pq1.pop();cout << pq1.top() << endl;//size()的使用,删除了一个元素,此时应该还有四个元素cout << pq1.size() << endl;return 0;
}

执行结果:

正如我们预料所得.

1.4、重写仿函数支持自定义数据类型

仿函数是通过重载‘()’运算符来进行模拟函数操作的类.

 通俗点说,仿函数是一个能行使函数功能类,然后类里必须实现“()”运算符重载.

比如我们要根据类里的某一个成员大小进行比较,因为是一个类,它不是C++里的基本数据类型,所以需要我们自己重新仿函数来支持它.

看下面这段程序

class Data
{
public:Data(int i, int d):id(d), data(d){}int GetData() const{return data;}
private:int id;int data;
};
//仿函数,从小到大排序,既大的优先级最高
class cmp
{
public:bool operator() (Data& d1, Data& d2){return d1.GetData() < d2.GetData();}
};
int main()
{//首先创建三个data类型数据Data* d1 = new Data(0, 1);Data* d2 = new Data(0, 2);Data* d3 = new Data(0, 3);//创建优先级队列,比较函数为cmp仿函数,并将数据全部pushpriority_queue<Data,vector<Data>,cmp> pq;pq.push(*d1);pq.push(*d2);pq.push(*d3);//全部输出出来while(!pq.empty()){cout << (pq.top().GetData()) << endl;pq.pop();}return 0;
}

二.priority_queue的模拟实现

2.1、构造&&重要的调整算法

priority_queue的底层结构就是堆,所以模拟实现只需要对堆封装即可.

所以其中大部分都是与之前堆的数据结构相关的一些方法.

我们知道priority_queue有三个参数来构造,所以我们也使用三个模板参数.

模板如下:

	template<class T,class Container = vector<T>,class Compare = less<T>>

然后一个类须有成员变量,这个类里只有一个成员变量

    Container _con; 

利用第二个模板参数既容器类型的构造了一个变量,这样就可以对里面的所有数据进行操作了.

准备就绪后,开始写构造函数,主要有两种:无参构造以及迭代器构造.

无参构造:其实可以不用写,但是由于有迭代器构造函数的存在,系统便不能再调用默认构造函数,所以必须自己手写一下无参的构造函数.

		priority_queue(){}

迭代器构造

和之前的迭代器构造方法一样,看一下便知.

		priority_queue(InputIterator first, InputIterator last){while (first != last){_con.push_back(*first);++first;}

于此同时,我们构造好数据后,还需要进行建堆,具体的建堆代码如下,和之前堆的数据结构中建堆的方法类似.

for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)//[_con.size()-1]是最后一个元素的下标,再-1然后/2是计算中间的元素,既最后一个结点的父节点{adjust_down(i);}

       既从数据中间的一个元素开始,每次进行向下调整算法,完毕之后--,指向下一个数据继续进行调整,如此直到第一个元素,建堆便完成.

提到了向下调整算法,这个方法在我之前堆的文章中有详细介绍过,大家可以去看之前的文章进行理解.

.

		void adjust_down(size_t parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1])++child;if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}elsebreak;}}

大概总固体思路是:先根据根节点找到孩子结点,然后判断左右两个孩子结点中的哪个大,默认是孩子结点是左孩子结点,如果右孩子结点比左孩子结点大,那么直接++即可,就是右孩子结点.

然后再判断,如果孩子结点的值大于父节点的值,则交换,并且更新父节点和孩子结点的值.

与此对应的是,既然有向下调整算法,那么也会有向上调整算法.

前面的文章也有写到过,不再详述.

		void adjust_up(size_t child){size_t parent = (child - 1) / 2;while (child > 0){if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}}

这个是传入孩子结点,我们根据公式求出父亲结点,公式就是代码中所写的那一个.

然后判断,符合条件更新父节点和子结点即可.

两大基础调整算法写完,那后面的就非常轻松了

2.2、常见接口的实现

这些接口都可以复用之前的调整算法.

push()

这个就是将数据插入,并且重新调整堆的结构.

		void push(const T& x){_con.push_back(x);adjust_up(_con.size()-1);//传入最后一个数据的下标}

对于push_back(),有人可能有疑问包括我就是,我没有实现push_back(),但为什么可以直接写呢

其实我们能用优先级队列的容器就那么几种,vector,deque等等,都是一些内部存储空间连续的,而这些容器都具有push_back()这个接口,所以到时候模板实例化的时候便可以用了. 

pop()

先把首尾元素进行交换,然后删除最后一个元素,再进行向下调整.

		void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}

top()

返回优先级最高既堆顶的元素,既容器的首元素.由于堆顶特性,堆中

		const T& top(){return _con[0];}

empty()、size()

这些都是容器中所对应拥有的函数,直接返回即可.

		bool empty() const{return _con.empty();}size_t size() const{return _con.size();}

 三.利用仿函数改进调整算法

通过我们上面写的向上调整,向下调整算法,发现有一个比较麻烦的地方

就是它其中的每次比较都是大于,既每次都是大顶堆

但是如果我们想要小顶堆怎么办?只能一点一点的改大小于符号 ,很容易就会混和忘记,非常的不方便.

这个时候我们便可以使用仿函数来解决这个问题.这个时候便用到刚开始写的三个模板参数中的第三个参数了.

可以先写两个仿函数,一个用来构造小顶堆,另外的是大顶堆.

template<class T>class less{public:bool operator()(const T& l, const T& r){return l < r;}};template<class T>class greater{public:bool operator()(const T& l, const T& r){return l > r;}};

      我们再把它应用到那两个调整算法里面.

adjust_up

最开始需要用仿函数构造一个对象,才可以使用.

			Compare com;

原来其中的:

				if (_con[child] > _con[parent])

改为:

				if (com(_con[child] , _con[parent]))

adjust_down

最开始需要用仿函数构造一个对象,才可以使用.

			Compare com;

原来其中的:

				if (child + 1 < _con.size() && _con[child] < _con[child + 1])
				if (_con[child] > _con[parent])

改为:

				if (child + 1 < _con.size() && com(_con[child] , _con[child + 1]))
				if (com(_con[child] , _con[parent]))

这样就改进完成了.以后想要改变大小顶堆时,只需要将Compare后面的仿函数改成自己需要的即可.

这就是优先级队列的所有内容了,包括使用及实现.

由于文章代码比较散乱,这里直接放上总代码方便参观.

#pragma once
#include<iostream>
#include<vector>using namespace std;
namespace hyx
{//大堆template<class T,class Container = vector<T>,class Compare = less<T>>class priority_queue{template<class T>class less{public:bool operator()(const T& l, const T& r){return l < r;}};template<class T>class greater{public:bool operator()(const T& l, const T& r){return l > r;}};public:priority_queue(){}template<class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_con.push_back(*first);++first;}//建堆for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){adjust_down(i);}}void adjust_up(size_t child){Compare com;size_t parent = (child - 1) / 2;while (child > 0){if (com(_con[child] , _con[parent])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}}void adjust_down(size_t parent){Compare com;size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child] , _con[child + 1]))++child;if (com(_con[child] , _con[parent])){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}elsebreak;}}void push(const T& x){_con.push_back(x);adjust_up(_con.size()-1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top(){return _con[0];}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}private:Container _con; };}

相关文章:

C++——优先级队列(priority_queue)的使用及实现

目录 一.priority_queue的使用 1.1、基本介绍 1.2、优先级队列的定义 1.3、基本操作(常见接口的使用&#xff09; 1.4、重写仿函数支持自定义数据类型 二.priority_queue的模拟实现 2.1、构造&&重要的调整算法 2.2、常见接口的实现 push() pop() top() empt…...

Linux学习记录——십일 环境变量

文章目录1、认识2、通过代码获取环境变量1、手动获取2、函数获取3、重新认识环境变量1、认识 在云服务器上写程序时&#xff0c;最终的执行需要./文件名&#xff0c;点表示当前目录&#xff0c;/是文件分隔符&#xff0c;之后就会打印程序&#xff0c;这是用户的操作&#xff…...

【人工智能 Open AI 】我们程序员真的要下岗了- 全能写Go / C / Java / C++ / Python / JS 人工智能机器人

文章目录[toc]人工智能 AI Code 写代码测试用golang实现冒泡排序用golang实现计算环比函数goroutine and channel用golang实现二叉树遍历代码用golang实现线程安全的HashMap操作代码using C programming language write a tiny Operation Systemuse C language write a tiny co…...

STM32 EXTI外部中断

本文代码使用 HAL 库。 文章目录前言一、什么是外部中断&#xff1f;二、外部中断中断线三、STM32F103的引脚复用四、相关函数&#xff1a;总结前言 一、什么是外部中断&#xff1f; 外部中断 是单片机实时地处理外部事件的一种内部机制。当某种外部事件发生时&#xff0c;单片…...

Mapper代理开发——书接MaBatis的简单使用

在这个mybatis的普通使用中依旧存在硬编码问题,虽然静态语句比原生jdbc都写更少了但是还是要写&#xff0c;Mapper就是用来解决原生方式中的硬编码还有简化后期执行SQL UserMapper是一个接口&#xff0c;里面有很多方法&#xff0c;都是一一和配置文件里面的sql语句的id名称所对…...

实体对象说明

1.工具类层Utilutil 工具顾明思义&#xff0c;util层就是存放工具类的地方&#xff0c;对于一些独立性很高的小功能&#xff0c;或重复性很高的代码片段&#xff0c;可以提取出来放到Util层中。2.数据层POJO对象&#xff08;概念比较大&#xff09; 包含了以下POJO plain ord…...

JAVA中加密与解密

BASE64加密/解密 Base64 编码会将字符串编码得到一个含有 A-Za-z0-9/ 的字符串。标准的 Base64 并不适合直接放在URL里传输&#xff0c;因为URL编码器会把标准 Base64 中的“/”和“”字符变为形如 “%XX” 的形式&#xff0c;而这些 “%” 号在存入数据库时还需要再进行转换&…...

改进YOLO系列 | ICLR2022 | OMNI-DIMENSIONAL DYNAMIC CONVOLUTION: 全维动态卷积

单个静态卷积核是现代卷积神经网络(CNNs)的常见训练范式。然而,最近的动态卷积研究表明,学习加权为其输入依赖注意力的n个卷积核的线性组合可以显著提高轻量级CNNs的准确性,同时保持高效的推理。然而,我们观察到现有的作品通过卷积核空间的一个维度(关于卷积核数量)赋予…...

信息收集之Github搜索语法

信息收集之Github搜索语法1.Github的搜索语法2.使用 Github 进行邮件配置信息收集3.使用Github进行数据库信息收集4.使用Github进行 SVN 信息收集5.使用Github进行综合信息收集在测试的信息收集阶段&#xff0c;可以去Github和码云上搜索与目标有关的信息&#xff0c;或者就有意…...

【案例教程】拉格朗日粒子扩散模式FLEXPART

拉格朗日粒子扩散模式FLEXPART通过计算点、线、面或体积源释放的大量粒子的轨迹&#xff0c;来描述示踪物在大气中长距离、中尺度的传输、扩散、干湿沉降和辐射衰减等过程。该模式既可以通过时间的前向运算来模拟示踪物由源区向周围的扩散&#xff0c;也可以通过后向运算来确定…...

试题 算法训练 自行车停放

问题描述 有n辆自行车依次来到停车棚&#xff0c;除了第一辆自行车外&#xff0c;每辆自行车都会恰好停放在已经在停车棚里的某辆自行车的左边或右边。(e.g.停车棚里已经有3辆自行车&#xff0c;从左到右编号为&#xff1a;3,5,1。现在编号为2的第4辆自行车要停在5号自行车的左…...

泛型与Map接口

Java学习之道 泛型 泛型这种参数类型可以用在类、方法和接口中&#xff0c;分别被称为泛型类&#xff0c;泛型方法&#xff0c;泛型接口 参数化类型&#xff1a;将类型由原来的具体的类型参数化&#xff0c;在使用/调用时传入具体的类型JDK5引入特性提供了安全检测机制&#xf…...

Unity Bug记录本

//个人记录&#xff0c;持续更新 1、将此代码挂载到空脚本上&#xff1a; bool flag (object)GetComponent<Camera>() null; bool flag1 (object)GetComponent<Text>() null; Debug.Log(flag"::"flag1); //输出结果&#xff1a;False::True bool…...

B. The Number of Products)厉害

You are given a sequence a1,a2,…,ana1,a2,…,an consisting of nn non-zero integers (i.e. ai≠0ai≠0). You have to calculate two following values: the number of pairs of indices (l,r)(l,r) (l≤r)(l≤r) such that al⋅al1…ar−1⋅aral⋅al1…ar−1⋅ar is neg…...

一起Talk Android吧(第五百一十二回:自定义Dialog)

文章目录整体思路实现方法第一步第二步第三步第四步各位看官们大家好&#xff0c;上一回中咱们说的例子是"自定义Dialog主题",这一回中咱们说的例子是" 自定义Dialog"。闲话休提&#xff0c;言归正转&#xff0c; 让我们一起Talk Android吧&#xff01;整体…...

GinVueAdmin源码分析3-整合MySQL

目录文件结构数据库准备配置文件处理config.godb_list.gogorm_mysql.gosystem.go初始化数据库gorm.gogorm_mysql.go开始初始化测试数据库定义实体类 Userserviceapi开始测试&#xff01;文件结构 本文章将使用到上一节创建的 CommonService 接口&#xff0c;用于测试连接数据库…...

大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——MapReduce开发总结

在编写MapReduce程序时&#xff0c;需要考虑如下几个方面&#xff1a; 1、输入数据接口&#xff1a;InputFormat 默认使用的实现类是&#xff1a;TextInputFormatTextInputFormat的功能逻辑是&#xff1a;一次读一行文本&#xff0c;然后将该行的起始偏移量作为key&#xff0…...

requests---(4)发送post请求完成登录

前段时间写过一个通过cookies完成登录&#xff0c;今天我们写一篇通过post发送请求完成登录豆瓣网 模拟登录 1、首先找到豆瓣网的登录接口 打开豆瓣网站的登录接口&#xff0c;请求错误的账号密码&#xff0c;通过F12或者抓包工具找到登录接口 通过F12抓包获取到请求登录接口…...

Python抓取数据具体流程

之前看了一段有关爬虫的网课深有启发&#xff0c;于是自己也尝试着如如何过去爬虫百科“python”词条等相关页面的整个过程记录下来&#xff0c;方便后期其他人一起来学习。 抓取策略 确定目标&#xff1a;重要的是先确定需要抓取的网站具体的那些部分&#xff0c;下面实例是…...

【Python学习笔记】第二十四节 Python 正则表达式

一、正则表达式简介正则表达式&#xff08;regular expression&#xff09;是一个特殊的字符序列&#xff0c;它能帮助你方便的检查一个字符串是否与某种模式匹配。正则表达式是对字符串&#xff08;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...