【C++】开始使用优先队列

news2024/9/21 5:36:19

在这里插入图片描述

送给大家一句话:
这世上本来就没有童话,微小的获得都需要付出莫大的努力。
– 简蔓 《巧克力色微凉青春》


开始使用优先队列

  • 1 前言
  • 2 优先队列
    • 2.1 什么是优先队列
    • 2.2 使用手册
    • 2.3 仿函数
  • 3 优先队列的实现
    • 3.1 基本框架
    • 3.2 插入操作
    • 3.3 删除操作
    • 3.4 其他函数
  • 4 总结
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 前言

上一篇文章我们实现了stack 与 queue 。接下来我们来认识一个新的容器:优先队列。优先队列具有一些与众不同的特性,也会涉及一种新的事物:仿函数。接下来我们一起来看看吧!

2 优先队列

2.1 什么是优先队列

优先队列是一种容器适配器容器适配器即将 特定容器类 (vector list 等等)封装作为其底层容器类 ),根据严格的弱排序标准,它的第一个元素总是所以元素中最大的!

弱排序标准
1. 两个关键字不能同时“严格弱序”于对方
2. 如果a“严格弱序”于b,且b“严格弱序”于c,则a必须“严格弱序”于c
3. 如果存在两个关键字,任何一个都不“严格弱序”于另一个,则这两个关键字是相等的。

也就是其性质类似与“堆”,可以在堆中随时插入元素,并且只能检索到当前所以元素的最大值或最小值(堆顶元素)。

优先队列 被实现为 容器适配器,queue提供一组特定的成员函数来访问其元素。元素从特定容器的"尾部"弹出,其称为优先队列的顶部。
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:

  1. empty():检测容器是否为空
  2. size():返回容器中有效元素个数
  3. front():返回容器中第一个元素的引用
  4. push_back():在容器尾部插入元素
  5. pop_back() : 删除容器尾部元素

标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

2.2 使用手册

函数声明接口说明
priority_queue()/priority_queue(first,last)构造一个空的优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回false
top( )返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

使用起来还是很简单的。
但是注意一下创建的时候需要将模版参数传够

template <class T, class Container = vector<T>,
  		class Compare = less<typename Container::value_type> > class priority_queue;
  • 模版参数 1 是 储存的数据类型
  • 模版参数 2 是 底层结构,一般使用vector 或 deque
  • 模版参数 3 是 仿函数,提供比较方式(建大堆,还是建小堆),下文我们来学习仿函数

2.3 仿函数

仿函数顾名思义是:类似函数但不是函数。
它是如何实现的呢??? 使用类中的将()操作符重载。就通过这样的类操作符的使用规避了函数指针。先前我们C语言的qsort 函数:

void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));

其最后一个参数就是函数指针,说实话比较复杂,因为我们在实现函数功能时并不知道会是什么类型,所以就很复杂。而我们通过仿函数,可以使用模版类,然后就自然适配所有的类型:

	//比较谁更小的的仿函数
	template<class T>
	struct less
	{
		bool operator()(const T& a, const T& b)
		{
			return a < b;
		}
	};
	//比较谁更大的的仿函数
	template<class T>
	struct greater
	{
		bool operator()(const T& a, const T& b)
		{
			return a > b;
		}
	};

通过这个仿函数可以轻松顶替复杂的函数指针。

3 优先队列的实现

3.1 基本框架

以下是优先队列的大概框架:

namespace bit
{

	template<class T>
	struct less
	{
		bool operator()(const T& a, const T& b)
		{
			return a < b;
		}
	};
	template<class T>
	struct greater
	{
		bool operator()(const T& a, const T& b)
		{
			return a > b;
		}
	};

	//默认是大堆
	template<class T , class Container = vector<T> , class compare = less<T> >
	class priority_queue
	{
	public:
		priority_queue() {};
		//迭代器构造
		template <class InputIterator>
	priority_queue(InputIterator first, InputIterator last){}
		//插入新元素向上调整
		void AdjustUp(){}
		//插入
		void push(const T &x){}
		//删除需要向下调整
		void AdjustDown(){}
		//删除
		void pop(){}
		//返回大小
		size_t size() const{}
		//取堆顶元素
		T& top(){	}
		//判断是否为空
		bool empty(){}

	private:
		//底层容器 实例化
		Container _con;
		//仿函数实例化
		compare com;

	};
}

我们接下来逐个来实现

3.2 插入操作

插入很简单,在容器尾push_back()一个新元素就可以,但是为了保持优先队列的特性我们需要对刚刚插入的元素进行向上调整,将其放在合适的位置上:


		void AdjustUp()
		{
			// 1 2 3 4 5 6 7 8 9
			//       0 
			//      /  \
			//     1    2
			//    / \  / \
			//   3  4 5   6
			//  / \
			// 7   8
			//孩子节点是刚插入的节点
			int child = _con.size() - 1;
			//父节点通过规律寻得 (看图分析就可以)
			int parent = (child - 1) / 2;
			//调整
			while (parent >= 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);
			AdjustUp();
		}

3.3 删除操作

注意删除操作是对堆顶的删除,但是容器的删除操作一般都是尾删,所以要先将容器的首元素与结尾位置进行交换,交换后尾差即可。然后进行向下调整,维持优先队列的特性。

		void AdjustDown()
		{
			// 1 2 3 4 5 6 7 8 9
			//       8
			//      /  \
			//     1    2
			//    / \  / \
			//   3  4 5   6
			//  / \
			// 7   [0]删除掉
			//父节点
			int parent = 0;
			//孩子节点
			int child = parent * 2 + 1;

			if (child + 1< _con.size() && com(_con[child], _con[child + 1]))
			{
				child++;//选择最合适的子节点
			}
			//调整
			while (child < _con.size() )
			{
				//谁大(小)谁当父节点
				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();
			AdjustDown();
		}

3.4 其他函数

其他函数直接使用底层容器进行返回就可以

		size_t size() const
		{
			return _con.size();
		}
		T& top()
		{
			return _con[0];
		}
		bool empty()
		{
			return _con.empty();
		}

4 总结

C++中的优先队列(priority_queue)是一种容器适配器,它提供了常数时间复杂度的元素插入操作和 logarithmic 时间复杂度的元素删除操作。由于它是基于堆实现的,所以非常适合用于需要频繁地找到最大或最小元素的应用场景。以下是一些典型的使用场景:

  1. 任务调度:在操作系统中,优先队列可以用来实现任务调度器(Linux下是使用优先队列),确保高优先级的任务先被执行。
  2. 图算法
    • Dijkstra算法:优先队列用于找出最短路径。
    • Prim算法:在生成最小生成树时,优先队列用于选择最小的边。
  3. 数据流处理:在处理数据流时,如在线广告投放系统,可以使用优先队列来选择价值最高的广告进行展示。
  4. 事件模拟:在模拟系统中,优先队列可以用来按时间顺序处理事件,比如网络中的数据包传输。
  5. 霍夫曼编码:在构建霍夫曼树时,优先队列用来按照频率排序字符。
  6. 多路归并:在数据合并操作中,优先队列可以帮助实现多路归并算法,例如在数据库索引的构建中。
  7. 堆排序:优先队列可以作为堆排序算法的实现基础。
  8. 选择问题:例如,快速选择算法可以使用优先队列来找到第k大的元素。
  9. 资源分配:在网络路由算法中,优先队列可以用来决定数据包的传输路径。
  10. 游戏开发:在游戏AI中,优先队列可以用来确定下一步的行动,基于行动的优先级进行排序。

优先队列的使用非常灵活,它适合于任何需要动态调整元素优先级和快速访问最高(或最低)优先级元素的场景。在使用时,需要注意其插入和删除操作的时间复杂度,以及如何根据实际需求选择合适的仿函数

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

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

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

相关文章

kubernetes的网络通信实现原理

网络原理 Kubernetes网络原理详解&#xff1a;一、Kubernetes 网络实现1.容器到容器&#xff08;同一Pod内&#xff09;通信流程&#xff1a;2. pod之间的通信&#xff08;以Calico为例&#xff09;&#xff1a; 二、CNI 网络模型三、网络策略四、开源的容器网络方案五、 常见网…

Linux管道共享内存

前言 进程虽然是独立运行的个体&#xff0c;但它们之间有时候需要协作才能完成一项工作&#xff0c;比如有两个进程需要同步数据&#xff0c;进程 A 把数据准备好后&#xff0c;想把数据发往进程 B&#xff0c;进程 B 必须被提前通知有数据即将到来&#xff0c;或者进程 A 想发…

Spark集群的搭建

1.1搭建Spark集群 Spark集群环境可分为单机版环境、单机伪分布式环境和完全分布式环境。本节任务是学习如何搭建不同模式的Spark集群&#xff0c;并查看Spark的服务监控。读者可从官网下载Spark安装包&#xff0c;本文使用的是spark-2.0.0-bin-hadoop2.7.gz。 1.1.1搭建单机版…

《乱弹篇(30)厌战的杜诗》

时下地球村有一伙成天叫嚣着“打打杀杀”、鼓吹快快发动战争的狂人&#xff0c;他们视老百姓的生命如草芥&#xff0c;毫不珍惜。没有遭受过战火焚烧的人&#xff0c;也跟着成天吠叫“快开战吧”。然而中国唐朝大诗人却是个“厌战派”&#xff0c;他对战争的厌恶集中表现在诗《…

实在RPA设计器试用导引

一、产品概述 实在RPA设计器是一款将人工智能(AI)与机器人流程自动化(RPA)深度融合的可视化自动流程编辑器。它通过AI推荐与桌面嵌入式交互&#xff0c;极大简化了RPA的使用难度&#xff0c;让普通业务人员也能轻松使用。实在RPA设计器具备以下核心优势&#xff1a; 兼容性&a…

我与C++的爱恋:类和对象(四)

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 ​ 朋友们大家好&#xff01;本篇是类和对象的最后一个部分。 一、static成员 声明为static的类成员称为类的静态成员&#xff0c;用static修饰的成员变量&#xff0c;称之…

系统架构最佳实践 -- 相关JAVA架构

1. java 类加载器架构 2. JVM 架构 3. Java 技术体系 4. 线程运行架构 5. Java 体系&#xff08;编译与运行&#xff09;结构 6. JMS 技术架构 7. JMX 技术架构 8. Spring 架构 9. Hibernate 架构 10. ibatis 架构 11. Struts2 架构 12. Struts1 架构 13. JBPM 14. EJB 技术架构…

Java面试八股之marshalling和demarshalling

marshalling和demarshalling Marshalling&#xff08;序列化&#xff09;是将内存中的对象状态转化为适合传输或存储的格式&#xff08;如字节流、JSON、XML&#xff09;&#xff0c;以便进行网络通信、持久化存储或跨平台/语言交互操作。Demarshalling&#xff08;反序列化&a…

渗透测试入门教程,从零基础入门到精通(非常详细)

目录 什么是渗透测试 渗透测试的重要性 渗透测试的前置技能 开始入门学习路线 什么是渗透测试 渗透测试&#xff0c;通常被视为模拟黑客的一种安全评估行为&#xff0c;其目的在于全面挖掘目标网站或主机的潜在安全漏洞。与真实的黑客攻击不同&#xff0c;渗透测试旨在发现…

9.MMD 基础内容总结及制作成品流程

前期准备 1. 导入场景和模型 在左上角菜单栏&#xff0c;显示里将编辑模型时保持相机和光照勾选上&#xff0c;有助于后期调色 将抗锯齿和各向异性过滤勾掉&#xff0c;可以节省资源&#xff0c;避免bug 在分辨率设定窗口&#xff0c;可以调整分辨率 3840x2160 4k分辨率 1…

05 MySQL--字段约束、事务、视图

1. CONSTRAINT 约束 创建表时&#xff0c;可以给表的字段添加约束&#xff0c;可以保证数据的完整性、有效性。比如大家上网注册用户时常见的&#xff1a;用户名不能为空。对不起&#xff0c;用户名已存在。等提示信息。 约束包括&#xff1a; 非空约束&#xff1a;not null检…

kafka实验部署

一、前期准备 二、kafka实验 在zookeeper后继续进行操作 2.1 为ndoe1、node2、node3作出部署 2.1.1 解压kafka压缩包&#xff08;node1举例&#xff09; 2.1.2 操作 将解压后的kafka移动到kafka&#xff0c;进入到kafka下的config中&#xff0c;复制文件 2.1.2.1 编辑server.pr…

LWIP开发之静态IP为什么接收和发送不了数据

使用的硬件开发板是探索者F4 V3版本 这里用的LWIP的lwIP例程7 lwIP_NETCONN_UDP实验 问了开发板的官方和其他人都说不清楚&#xff1b;搞了两天&#xff0c;浪费了两天时间&#xff1b; 最奇葩的问题还在于只能单片机发送&#xff0c;上位机能接收。而上位机发送单片机不能接…

虚拟机扩容方法

概述 我的虚拟机开始的内存是40G,接下来要扩成60GB 扩容步骤 步骤1 步骤2 步骤3 修改扩容后的磁盘大小&#xff0c;修改后的值只可以比原来的大&#xff0c;修改完成后点击扩展&#xff0c;等待扩展完成 步骤4 虽然外面扩展成功&#xff0c;但是新增的磁盘空间虚拟机内部还…

自媒体个人品牌IP策划打造孵化运营方案

【干货资料持续更新&#xff0c;以防走丢】 自媒体个人品牌IP策划打造孵化运营方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 ppt可编辑&#xff08;完整资料包含以下内容&#xff09;目录个人IP孵化方案概要&#xff1a; 1. 目标定位与市场分析 - 女性…

QMT和Ptrade有什么区别?该如何选择?

QMT&#xff08;Quantitative Model Trading&#xff09;和Ptrade&#xff08;Professional Trading&#xff09;是两种不同的交易策略和方法&#xff0c;它们在金融市场中被广泛应用。了解它们的区别有助于投资者根据自己的需求和目标做出选择&#xff1a; QMT&#xff08;量…

AI+PS快捷键大全!

hello&#xff0c;我是小索奇&#xff0c; 你会用Photoshop&#xff08;PS&#xff09;或者&#xff08;Illustrator&#xff09;AI吗&#xff1f;相信很多人都会接触到吧&#xff0c;但有一部分人很少用快捷键&#xff0c;仅凭借鼠标点击来实现功能&#xff0c;殊不知快捷键能…

在线拍卖系统,基于SpringBoot+Vue+MySql开发的在线拍卖系统设计和实现

目录 一. 系统介绍 二. 功能模块 2.1. 管理员功能模块 2.2. 用户功能模块 2.3. 前台首页功能模块 2.4. 部分代码实现 一. 系统介绍 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系…

感知机学习算法中的Novikoff定理证明中的隐含背景知识

一、引言 《统计学习方法》&#xff08;李航著&#xff09;第二章感知机学习时&#xff0c;其中的Novikoff定理是关于感知机算法收敛性的一个重要定理。这个定理保证了对于线性可分的数据集&#xff0c;感知机学习算法最终能够收敛到一个解&#xff0c;即存在一个权重向量 w 和…

投稿没被采纳不是水平问题可能是投稿方法不对

每周一次的信息宣传投稿任务,如同一面镜子,映照出我们在媒体传播领域的探索与成长。起初,我在执行这项任务时,怀着满腔热情,挥洒汗水创作出一篇篇通信稿件,却在投稿阶段屡遭挫折,多次尝试均未果,一度让我质疑自己的写作能力是否足以胜任这项工作。 那时,我采用的是传统的邮箱投…