【C++】STL中优先级队列的使用与模拟实现

前言:在前面我们学习了栈和队列的使用与模拟实现,今天我们来进一步的学习优先级队列使用与模拟实现

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:高质量C++学习 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


目录标题

  • 什么是优先级队列
    • 优先级的队列常见功能的使用方式
    • 什么是仿函数
  • 优先级队列的底层实现(适配器版本)
    • 优先级队列的基本框架
      • push():将元素入队列
      • pop():删除队列中优先级最高的元素
      • top():获取并返回队列中优先级最高的元素
      • size():获取并返回队列大小
      • empty():判断队列是否为空
    • 整体代码


什么是优先级队列

C++中的优先级队列是一种特殊的数据结构,它类似于队列,但是元素按照优先级进行排序。在优先级队列中,元素的插入被赋予了一个优先级值,具有较高优先级的元素将排在较低优先级的元素之前(用大白话讲,就是你队列中的元素按照某种要求进行了对应的排序)。

C++中的优先级队列通常使用堆(heap)作为底层实现,可以是最小堆或最大堆。最小堆意味着优先级值较小的元素具有较高的优先级,而最大堆则相反。

优先级队列的主要操作是插入和删除最高优先级的元素。在C++中,可以使用std::priority_queue模板类来实现优先级队列。该模板类提供了一些成员函数,如push()、pop()和top()等,用于插入、删除和获取最高优先级的元素。


优先级的队列常见功能的使用方式

1.插入元素:使用push()函数向优先级队列插入元素(调用前需要导入头文件queue)

int main()
{priority_queue<int> s1;//创建一个对象s1.push(10);//入队列,且队列中的元素会默认按照降序进行排序s1.push(20);s1.push(0);s1.push(9);s1.push(120);return 0;
}

  1. 删除最高优先级元素:使用pop()函数删除优先级队列中的最高优先级元素(可以理解成删除队列中排序过后的队头的元素)。
int main()
{priority_queue<int> s1;s1.push(10);//入队列s1.push(120);s1.pop();//删除队头的元素return 0;
}

  1. 获取最高优先级元素:使用top()函数获取优先级队列中的最高优先级元素(通俗的理解获取队列排序过后的队头元素)。
int main()
{priority_queue<int> s1;s1.push(10);//入队列s1.push(9);s1.push(120);s1.pop();//出队列auto num = s1.top();//获取队列的队头的元素cout << num << endl;return 0;
}
//运行结果:10

  1. 获取队列中的元素数量:使用size()函数获取优先级队列中的元素数量。
int main()
{priority_queue<int> s1;s1.push(10);//入队列cout << s1.size() << endl;//输出队列中元素的个数return 0;
}

  1. 判断队列是否为空:使用empty()函数判断优先级队列是否为空。
int main()
{priority_queue<int> s1;s1.push(10);s1.push(20);s1.push(0);s1.push(9);s1.push(120);auto num = s1.top();cout << num << endl;cout << s1.size() << endl;if (!s1.empty())//判断队列是否有元素{cout << "队列中有元素" << endl;}else cout << "队列中没有元素" << endl;return 0;
}

需要注意的是,std::priority_queue默认以降序排序,即最大元素具有最高优先级。如果希望以升序排序,则可以使用自定义的比较函数或者使用std::greater作为模板参数

// 使用自定义的比较函数,greater即默认升序的排序方式,也可以调用自己写的排序方式
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;// 或者使用std::greater模板参数
std::priority_queue<int, std::vector<int>, std::greater<>> pq;


什么是仿函数

C++中的仿函数是一个类或者结构体,实现了函数调用操作符(operator())的重载。通过重载函数调用操作符,可以使得仿函数对象像函数一样被调用。仿函数常用于算法中,用于实现特定的操作或者运算。它可以接受一个或多个参数,并返回一个结果。通过仿函数,可以实现函数对象的状态的维护,以及在算法中对元素进行处理的定制化操作。
例子:

template <class T>
class less//通过类来判断他的大小
{
public:operator() (const T& x, const T& y){return x < y;}
};template <class T>
class greater//同理
{
public:operator() (const T& x, const T& y){return x > y;}
};int main()
{int num1 = 10, num2 = 20,num3 = 30,num4 = 50;less<int> s1;greater<int> s2;if (s1(num1, num2))//s1()就类似与之前的一个函数的样子,我们调用s1这个对象即可完成你想要指定的操作{cout << "num1 < num2" << endl;}if (s2(num4, num3))//同理你调用s2也可以类似于函数的感觉,去完成你想要指定完成的操作{cout << "num4 > num3" << endl;}return 0;
}

优先级队列的底层实现(适配器版本)

前言:这里我们需要先知道刚刚我们使用了那些功能,这里方便我们对其进行模拟实现。
基本操作:
1、push():将元素入队列。【将元素入队列,并且让其按照升序或者降序进行某种方式进行排序】

2、pop():删除队列中优先级最高的元素。【队首出队列,并且依然要保持队列的完整性】

3、top():获取并返回队列中优先级最高的元素。

4、size():获取并返回队列大小。【返回值为 unsigned int(size_t)类型】

5、empty():判断队列是否为空。【返回值为bool类型。队列为空返回true,不空返回false】


优先级队列的基本框架

namespace bit
{template<class T>class less//通过类来实现仿函数,来帮助我们实现运算符重载达到控制升序降序的目的{public:bool operator()(const T& x, const T& y){return x < y;}};template<class T>class greater//通过仿函数来帮助我们实现升序和降序{public: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:private:Container _con;//调用适配器}
}

push():将元素入队列

代码思路:我们的适配器默认调用的是vector因为他内部的接口可以调用尾插,同理我们这里直接调用尾插的函数即可,但是我们提到过这个是一个优先级队列,优先级队列的底层就是一个,那么我们对堆插入元素的时候在数据结构讲过我们需要对其进行调整,否则这个堆就会失去他原有的完整性,如果对堆这块内容不太熟悉的小伙伴可以去看看我之前的博客堆的模拟实现

void push(const T& x)//入队列
{_con.push_back(x);//需要保证队列里面的优先级的顺序,因此我们需要对其进行向上调整adjust_up(_con.size() - 1);//当前的元素个数减一就是当前孩子结点下标,然后将他向上调整即可完成插入
}//向上调整
void adjust_up(size_t child)//插入的是孩子看孩子是否比父亲还大,比父亲还大就交换,且是默认大堆
{Compare com;int parent = (child - 1) / 2;//父亲结点的下标while (child > 0){//巧妙的利用仿函数对其进行默认的降序进行排序,也可以通过传great进行升序排序if (com(_con[parent], _con[child]))//比父亲大就交换{swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;//孩子不比父亲大,说明就是当前的位置}}
}

pop():删除队列中优先级最高的元素

代码思路:在之前讲解堆的时候我们知道,要想删除堆顶的元素,我们通常的做法是将堆顶的元素和尾元素进行交换,然后在删除此时堆尾部的元素,进行删除,然后在将交换后的堆顶的元素向下调整保持堆的完整性,因此我们在优先级队列中需要用到同样的做法,将队首和队尾的元素进行交换,然后删除队尾的元素,然后向下调整即可。

void pop()//队头出队列
{swap(_con[0], _con[_con.size() - 1]);//将堆的底部元素和堆顶的交换,然后删除堆底部元素就完成了队头出队列_con.pop_back();//删除堆顶元素adjust_down(0);//向下调整交换过后的堆,即依然保持堆的完整性
}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[parent], _con[child]))//父亲比孩子小,就向下调整{swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}

top():获取并返回队列中优先级最高的元素

代码思路:我们知道优先级队列的底层就是个堆,队首的那个元素就是堆顶的元素,因此直接返回下标为0的首元素即可。

const T& top()//获取队首元素
{return _con[0];
}

size():获取并返回队列大小

代码思路:这里我们直接调用容器中的函数即可

size_t size()//查看队列中元素的个数
{return _con.size();
}

empty():判断队列是否为空

代码思路:同理调用容器中的函数即可。

bool empty()//判断是否为空,依然玩适配器的那套玩法
{return _con.empty();
}

整体代码

#include <iostream>
#include<vector>
#include<assert.h>
using namespace std;namespace bit
{template<class T>class less{public:bool operator()(const T& x, const T& y){return x < y;}};template<class T>class greater{public: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;int parent = (child - 1) / 2;//父亲结点的下标while (child > 0){if (com(_con[parent], _con[child]))//比父亲大就交换{swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;//孩子不比父亲大,说明就是当前的位置}}}void push(const T& x)//入队列{_con.push_back(x);adjust_up(_con.size() - 1);//vector的元素个数减一就是当前孩子结点下标,然后将他向上调整即可完成插入}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[parent], _con[child]))//父亲比孩子小,就向下调整{swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}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();}const T& top()//获取队首元素{return _con[0];}private:Container _con;};void test1(){bit::priority_queue<int, vector<int>, bit::greater<int>> pq;pq.push(2);pq.push(1);pq.push(4);pq.push(3);pq.push(7);pq.push(8);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;}void test2(){priority_queue<int> pq;//默认大堆pq.push(2);pq.push(1);pq.push(4);pq.push(3);pq.push(7);pq.push(8);while (pq.size()){cout << pq.top() << " ";pq.pop();}cout << endl;}
}emplate <class T>
class less
{
public:operator() (const T& x, const T& y){return x < y;}
};template <class T>
class greater
{
public:operator() (const T& x, const T& y){return x > y;}
};int main()
{int num1 = 10, num2 = 20,num3 = 30,num4 = 50;less<int> s1;greater<int> s2;if (s1(num1, num2)){cout << "num1 < num2" << endl;}if (s2(num4, num3)){cout << "num4 > num3" << endl;}return 0;
}//int main()
//{
//	//bit::test1();
//	//bit::test2();
//	return 0;
//}

好啦,今天的内容就到这里啦,下期内容预告C++中的模板大全解.


结语:前段时间博主又又又去忙学校的事情和一些比赛啥的,这段时间会猛猛干的!。


🌏🗺️ 这里祝各位接下来的每一天好运连连 💞💞

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

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

相关文章

常见的8种排序(含代码):插入排序、冒泡排序、希尔排序、快速排序、简单选择排序、归并排序、堆排序、基数排序

时间复杂度O(n^2) 1、插入排序 (Insertion Sort) 从第一个元素开始&#xff0c;该元素可以认为已经被排序&#xff1b;取出下一个元素&#xff0c;在已经排序的元素序列中从后向前扫描&#xff1b;如果该元素&#xff08;已排序&#xff09;大于新元素&#xff0c;将该元素移到…

IPv6 address status lifetime

IPv6 地址状态转换 Address lifetime (地址生存期) 每个配置的 IPv6 单播地址都有一个生存期设置&#xff0c;该设置确定该地址在必须刷新或替换之前可以使用多长时间。某些地址设置为“永久”并且不会过期。“首选”和“有效”生存期用于指定其使用期限和可用性。 自动配置的…

vue中的状态管理

第1部分&#xff1a;引言 状态管理是应用中数据流动和变更的核心机制。在Vue应用中&#xff0c;状态管理不仅涉及到组件间的数据共享&#xff0c;还包括了数据的持久化、异步操作的处理等复杂场景。良好的状态管理策略可以提高应用的响应速度&#xff0c;降低组件间的耦合度&a…

分页查询前端对接

文章目录 添加角色修改角色当点击修改按钮后,那么就会弹出对话框,所以要设置显示为true点击修改的时候就是 要显示对话框 制作用户管理页面开发后端接口用户查询前端整合新增接口功能实现修改 添加角色 首先添加 添加表单的组件 那么总结一下 就是使用 组件 然后再使用变量接…

Python基础入门

目录 1. 什么是Python&#xff1f; 2. 安装Python 3. Python基础语法 4. 数据结构 5. 文件操作 6. Python标准库 总结 1. 什么是Python&#xff1f; Python是一种高级编程语言&#xff0c;由Guido van Rossum于1991年发布。它以其简单易读的语法和强大的功能而闻名&…

Nominatim免费的地址解析,逆地址解析,OpenStreetMap开源地图数据【全网最全】

视频学习地址 国内的一些地址解析供应商的API都开始付费了&#xff0c;就想找个免费的地址解析和逆地址解析的应用&#xff0c;最终选择了Nominatim OpenStreetMap 文章目录 一、选型1-1、数据源1-2、地理编码引擎2-1、初尝Nominatim2-1-1、地址解析2-1-2、逆地址解析 2-2、OS…

国内外大模型生态发展报告!

很多同学只知类似Check GPT或者说对国内的一些比较了解&#xff0c;对国外的不太了解&#xff0c;所以在这总结。 1 大模型的发展 左表 名称参数特点发布时间GPT-215亿英文底模&#xff0c;开源2019年Google T5110亿多任务微调, 开源2019年GPT-3.51750亿人工反馈微调2022年M…

UFS Power Mode Change 介绍

一. UFS Power Mode Change简介 1.UFS Power Mode指的是Unipro层的Power State, 也可以称为链路(Link)上的Power Mode, 可以通过配置Unipro Attribute, 然后控制切换Unipro Power State, 当前Power Mode Change有两种触发方式&#xff1a; (1) 通过DME Power Mode Change触发…

java中实现Callable方式创建线程

一、为啥要引入Callable 在前面讲了通过继承Thread和实现Runnable方式创建线程的区别&#xff0c;那为什么有了Runnable还要引入Callable?下面通过实现Runnable方式的弊端给出答案 实现Runnable方式的弊端&#xff1a; package java.lang; FunctionalInterface public inte…

1095 解码PAT准考证(测试点3)

solution 测试点3超时&#xff1a;命令为3时&#xff0c;用unordered_map而非map&#xff0c;否则会超时 #include<iostream> #include<string> #include<algorithm> #include<unordered_map> using namespace std; const int maxn 1e4 10; struct…

2024山东大学软件学院创新项目实训(9)使用OpenCompass进行模型评估

下载好OpenCompassData-core-20231110.zip 之后&#xff0c;解压压缩包 unzip OpenCompassData-core-20231110.zip 运行代码&#xff1a; python run.py --datasets ceval_gen --hf-path /hy-tmp/7B21/merged --tokenizer-path /hy-tmp/7B21/merged --tokenizer-kwargs p…

步步精:连接器领域的卓越品牌

自1987年成立以来&#xff0c;步步精坐落于美丽的旅游城市——温州市乐清虹桥镇&#xff0c;被誉为“国家电子主体生产基地”、“国家精密模具制造基地”。公司拥有7大厂区、9大事业部&#xff0c;800名专职员工&#xff0c;致力于提供高品质的连接器解决方案。注册商标“BBJCO…

百度ai人脸识别项目C#

一、项目描述 本项目通过集成百度AI人脸识别API&#xff0c;实现了人脸检测和识别功能。用户可以上传图片&#xff0c;系统将自动识别人脸并返回识别结果。 二、开发环境 Visual Studio 2019或更高版本.NET Framework 4.7.2或更高版本AForge.NET库百度AI平台人脸识别API 三、…

从网络配置文件中提取PEAP凭据

我的一位同事最近遇到了这样一种情况&#xff1a;他可以物理访问使用802.1X连接到有线网络的Windows计算机&#xff0c;同时保存了用于身份验证的用户凭据&#xff0c;随后他想提取这些凭据&#xff0c;您可能认为这没什么特别的&#xff0c;但是事情却有点崎岖波折…… 如何开…

攻防世界-5-1

下载文件发现是一个没有尾缀的文件&#xff0c;扔winhex&#xff0c;emmmm还是没看出来 搜了一圈&#xff0c;发现用xortool 得到key之后&#xff0c;跑一下脚本 得到flag&#xff1a; wdflag{You Are Very Smart}

pytest测试框架pytest-sugar插件生成进度条

Pytest提供了丰富的插件来扩展其功能&#xff0c;介绍下插件pytest-sugar&#xff0c;可以帮助我们在控制台中显示彩色的测试结果和进度条&#xff0c;提供失败的堆栈回溯信息。 为了使用 pytest-sugar&#xff0c;需要满足以下条件&#xff1a; Python 3.8 或更高版本pytest…

并行计算之SIMD与SPMD

SIMD (Single Instruction Multiple Data) SIMD&#xff0c;也就是单指令多数据计算&#xff0c;一条指令可以处理多个数据。通过向量寄存器存储多个数据元素&#xff0c;并使用单条指令同时对这些数据元素进行处理&#xff0c;从而提高了计算效率。 代码示例&#xff1a; fl…

联想Y7000P 2023款拆机教程及升级内存教程

0.电脑参数介绍 联想Y7000P 2023电脑&#xff0c;笔者电脑CPU为i7-13700H&#xff0c;14核20线程&#xff1b;标配内存为三星的DDR5-5600MHz-8GB*2&#xff0c;由于电脑CPU限制&#xff0c;实际内存跑的频率为5200MHz; 2个内存插槽&#xff0c;2个固态硬盘插槽。每个内存插槽最…

FineReport报表案例

普通报表 保存的文件类型为 cpt&#xff0c;依靠着单元格的扩展与父子格的关系来实现模板效果&#xff0c;可进行参数查询&#xff0c;填报报表&#xff0c;图表设计等等&#xff0c;但是在分页预览模式下不能在报表主体中展示控件&#xff0c;而且单元格间相互影响&#xff0c…

1.2 DataX 数据同步工具详细介绍

DataX 是阿里巴巴开源的一款高效的数据同步工具&#xff0c;旨在实现多种异构数据源之间的高效数据同步。以下是对 DataX 的详细介绍&#xff1a; 架构 DataX 的架构主要包括以下几个核心组件&#xff1a; DataX Core&#xff1a;负责任务调度、插件加载、日志管理等核心功能…