STL ——priority_queue的模拟实现与基本使用 | 仿函数的介绍| 容器适配器的介绍

news2024/11/26 22:29:19

了解priority_queue

在这里插入图片描述

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特
    定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭
    代器访问,并支持以下操作:
  • empty():检测容器是否为空
  • size():返回容器中有效元素个数
  • front():返回容器中第一个元素的引用
  • push_back():在容器尾部插入元素
  • pop_back():删除容器尾部元素
  1. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  2. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆

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

priority_queue的定义方式

1.不指定底层容器和内部需要构造的堆结构。(默认大堆)

priority_queue<int> q1;

2.使用vector作为底层容器,内部构造小堆结构

priority_queue<int, vector<int>, greater<int>> q2;

3.使用vector作为底层容器,内部构造大堆结构(显示定义)

priority_queue<int, vector<int>, less<int>> q3;

其他的接口使用和STL的这些容器的方法都差不多,就不再赘述
在这里插入图片描述

priority_queue的模拟实现

向上调整算法和向下调整算法

在模拟实现之前,回顾一下堆的上调整算法和向下调整算法
在这里插入图片描述
在之前C语言实现的向下调整算法和向上调整算法

//用于pop堆顶,将堆顶的数据与堆尾交换,然后pop堆尾,再调整堆
void AdjustDown(HPDataType* a, int n, int parent)
{
	assert(a);
	int child = 2 * parent + 1;
	while (child<n)
	{
		if (a[child] < a[child + 1])
		{
			child++;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}
//插入小堆的数向上调整
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	//while (parent >= 0)  不好
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

这里在c++中我们直接在类内实现成员函数,就不用传入这么多参数了

template<class T,class Container = std::vector<T>>
	class priority_queue {
	public:
		//向上调整 一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法
		void Adjust_up(int child) {
			int parent = (child - 1) / 2;
			while (child>0) {
				if (_con[child] > _con[parent]) {
					swab(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else {
					break;
				}
			}
		}
		//向下调整 用于pop堆顶,将堆顶的数据与堆尾交换,然后pop堆尾,再调整堆
		void Adjust_down(int parent){
			int child = 2 * parent + 1;
			while (child < _con.size()) {
				if (child + 1 < _con.size() && _con[child + 1] > _con[child]) {
					++child;
				}
				if (_con[child] > _con[parent]) {
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * parent + 1;
				}
				else {
					break;
				}
			}
		}
	private:
		Container_con;
	};

常用接口实现

void push(const T& x) {
			_con.push_back(x);
			Adjust_up(_con.size()-1);
		}
		void pop() {
			if (!_con.empty()) {
				swap(_con[0], _con[_con.size() - 1]);
				_con.pop_back();
				Adjust_down(0);
			}
		}
		bool empty() {
			return _con.empty();
		}
		const T& top() const {
			return _con[0];
		}
		size_t size() {
			return _con.size();
		}

在这里插入图片描述

仿函数

在c++标准库中的介绍中,优先级队列的模板参数定义为
template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> >
在上面的实现中,是大堆,打印出来是降序排列的,如果我们想要其是小堆怎么办呢?总不能再写一个小堆的向上调整和向下调整算法吧

所以这里就引入了仿函数,也就是这个模板的第三个参数class Compare = less

了解仿函数

在STL源码剖析中对仿函数给出了定义
在这里插入图片描述
光看定义肯定是看不懂的,我们直接用例子来说明:

struct lesscmp {
	bool operator()(int x, int y) {
		return x < y;
	}
};
int main() {
	int a = 10, b = 20;
	lesscmp cmp;
	cout << cmp(a, b);
}

在这里插入图片描述
当然这里的仿函数也可以使用模板,效果都是一样的

template<class T>
struct lesscmp {
	bool operator()(T x, T y) {
		return x < y;
	}
};
int main() {
	int a = 10, b = 20;
	lesscmp<int> cmp;
	cout << cmp(a, b);
}

引入仿函数的向上调整算法和向下调整算法

// less: 小于的比较
	template<class T>
	struct less {
		bool operator()(const T& x, const T& y) const {
			return x < y;
		}
	};
	// greater: 大于的比较
	template<class T>
	struct greater {
		bool operator()(const T& x, const T& y) const {
			return x > y;
		}
	};
	template<class T,class Container = std::vector<T>, class Compare = less<T>>
	class priority_queue {
	public:
		//向上调整 一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法
		void Adjust_up(int child) {
			size_t parent = (child - 1) / 2;
			while (child>0) {
				/*if (_con[child] > _con[parent])*/
				if (_cmp(_con[child],_con[parent]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else {
					break;
				}
			}
		}
		//向下调整 用于pop堆顶,将堆顶的数据与堆尾交换,然后pop堆尾,再调整堆
		void Adjust_down(int parent) {
			size_t child = 2 * parent + 1;
			while (child < _con.size()) {
				/*if (child + 1 < _con.size() && _con[child + 1] > _con[child]) */
				if (child + 1 < _con.size() && _cmp(_con[child + 1], _con[child]))
				{
					++child;
				}
				/*if (_con[child] > _con[parent])*/
				if (_cmp(_con[child],_con[parent]))
				{
					swap(_con[parent], _con[child]);
						parent = child;
						child = 2 * parent + 1;
				}
				else {
					break;
				}
			}
		}
private:
		Container _con;
		Compare _cmp;
	};

在这里插入图片描述

容器适配器

什么是适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
通俗的说,就是转换器,类似于我们平常用的usb转接线之类的
在这里插入图片描述

容器适配器的作用

  • 改变容器的接口
  • 增加容器的功能
  • 限制容器的功能

之前的栈,队列和这里的优先级队列就是用的容器适配器,将vector,deque等容器转换成了这些类型的容器

容器适配器的优点

封装性
容器适配器隐藏了底层容器的实现细节,只暴露出特定的接口,使得使用者可以方便地操作容器适配器,而不需要了解底层容器的具体实现。

灵活性
容器适配器可以根据不同的需求选择不同的底层容器来实现功能。例如,可以使用栈来实现适配器,也可以使用队列来实现适配器,这取决于具体的使用场景和要求。

功能拓展
容器适配器可以根据需要进行扩展,添加新的功能或修改现有功能。由于适配器与底层容器解耦,因此可以独立地对适配器进行修改,而不会影响到其他部分的代码。

与标准库兼容
容器适配器通常与标准库的容器接口兼容,这意味着可以通过容器适配器来替换标准容器的使用,而不需要修改其他代码。

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

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

相关文章

JetBot手势识别实验

实验简介 本实验目的在JetBot智能小车实现手势识别功能&#xff0c;使用板卡为Jetson Nano。通过小车摄像头&#xff0c;识别五个不同的手势&#xff0c;实现小车的运动及灯光控制。 1.数据采集 连接小车板卡的Jupyterlab环境&#xff0c;运行以下代码块&#xff0c;配置数据…

Go Energy 实现的跨平台桌面(GUI)应用介绍

关于 Energy Energy是Go语言基于LCL和CEF开发的跨平台桌面应用框架 系统支持 Windows 系列 XP SP3 到 Windows 11, Linux&#xff0c;MacOS. 版本 当前版本2.x 底层动态链接库 liblcl LCL: Lazarus 跨平台 GUI LCL 组件库, 包含了大量的系统原生GUI控件, 多达几百个控件. 在…

快速体验 Llama3 的 4 种方式,本地部署,800 tokens/s 的推理速度真的太快了!

北京时间4月19日凌晨&#xff0c;Meta在官网上官宣了Llama-3&#xff0c;作为继Llama1、Llama2和CodeLlama之后的第三代模型&#xff0c;Llama3在多个基准测试中实现了全面领先&#xff0c;性能优于业界同类最先进的模型&#xff0c;你有没有第一时间体验上呢&#xff0c;这篇文…

DS:单链表的实现

欢迎各位来到 Harper.Lee 的编程学习小世界&#xff01; 博主主页传送门&#xff1a;Harper.Lee的博客 我将在这里分享我的学习过程等心得 创作不易&#xff0c;码字不易&#xff0c;兄弟们养成先赞后看的好习惯哦&#xff01; 想一同进步的uu&#xff0c;可以来后来找我哦&…

20232937文兆宇 2023-2024-2 《网络攻防实践》实践七报告

20232937文兆宇 2023-2024-2 《网络攻防实践》实践七报告 1.实践内容 &#xff08;1&#xff09;使用Metasploit进行Linux远程渗透攻击 任务&#xff1a;使用Metasploit渗透测试软件&#xff0c;攻击Linux靶机上的Samba服务Usermap_script安全漏洞&#xff0c;获取目标Linux…

深入Linux下的GCC编译器:从入门到精通

目录标题 1、GCC编译器概述2、安装GCC3、GCC的基本使用4、高级功能4.1 多文件编译4.2 静态和动态链接4.3 什么是链接&#xff1f;4.4 静态链接优点缺点 4.5 动态链接优点缺点 4.6 实际应用4.7 编译优化 GCC&#xff08;GNU Compiler Collection&#xff09;是一款免费、开源的编…

累积流量计算(MODBUS RTU通信数据处理)

1、常用通信数据处理 MODBUS通信系列之数据处理_modbus模拟的数据变化后会在原来的基础上累加是为什么-CSDN博客文章浏览阅读1k次,点赞2次,收藏2次。MODBUS通信专栏有详细文章讲解,这里不再赘述,大家可以自行查看。链接如下:SMART S7-200PLC MODBUS通信_RXXW_Dor的博客-C…

操作系统(Operating System)知识点复习——第十一章 I/O管理与磁盘调度

目录 0.前言 1.I/O设备 2.I/O功能的组织 3.Operating System Design Issues 4.I/O缓冲 4.1 单缓冲Single Buffer 4.2 双缓冲Double Buffer 4.3 循环缓冲 5.磁盘调度Disk Scheduling 5.1 磁盘性能参数 5.2 磁盘调度策略 ①First-in&#xff0c;first-out(FIFO) ②Pr…

芯片胶点胶加工的效果和质量的检测方法有哪些?

芯片胶点胶加工的效果和质量的检测方法有哪些&#xff1f; 芯片胶在电子封装领域用的是比较多的&#xff0c;特别是高度精密集成芯片器件。那么如何判断点胶后的效果和质量的好与坏&#xff1f; 芯片胶点胶加工的效果和质量的检测是一个重要的环节&#xff0c;以确保产品满足设…

医院能耗监测管理系统,助力医院节能减排

医院属于大型建筑&#xff0c;由于医院能耗计量点位繁多&#xff0c;数据采集大多采用传统的人工模式&#xff0c;很难保证计量管理的准确性和科学性。为了对医院能耗进行精细化管理&#xff0c;需要建立能耗管理系统&#xff0c;在辅助成本核算工作的同时&#xff0c;可以实时…

Java学习笔记29(泛型)

1.泛型 ArrayList<Dog> arrayList new ArrayList<Dog>(); //1.当我们ArrayList<Dog>表示存放到ArrayList集合中的元素是Dog类 //2.如果编译器发现添加的类型&#xff0c;不满足要求&#xff0c;就会报错 //3.在便利的时候&#xff0c;可以直接取出Dog类型而…

8个拿来即用的Python自动化脚本!

每天你都可能会执行许多重复的任务&#xff0c;例如阅读新闻、发邮件、查看天气、清理文件夹等等&#xff0c;使用自动化脚本&#xff0c;就无需手动一次又一次地完成这些任务&#xff0c;非常方便。而在某种程度上&#xff0c;Python 就是自动化的代名词。 今天分享 8 个非常…

BGP的基本配置

l 按照以下步骤配置BGP协议&#xff1a; 第1步&#xff1a;设备基本参数配置&#xff0c;AS内配置IGP确保内部网络连通性&#xff1b; l 配置IGP&#xff08;OSPF协议等&#xff09;路由解决peer对等体的源和目标IP之间连通性&#xff0c;确保peer之间TCP&#xff08;179&a…

如何查看自己的公网IP?

我们在网络中&#xff0c;每一个设备都被分配了一个唯一的IP地址&#xff0c;用以区分和识别其他设备。公网IP地址是指可被公众访问的IP&#xff0c;是因特网上的全球唯一标识。当我们需要查看自己的公网IP时&#xff0c;可以采取以下几种方式。 使用命令行查看公网IP 在Windo…

SpringCloud 之 服务提供者

前提 便于理解,我修改了本地域名》这里!!! 127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com 127.0.0.1 eureka7003.com学习Rest实例之提供者 提供者模块展示 1、导入依赖 <!-- 实体类 Web--><dependency><groupId>com.jyl</groupId><…

光电离子传感器PID-AH5在空气质量监测和HVAC系统中的应用

随着工业化和城市化的步伐不断加快&#xff0c;空气质量问题日益严重&#xff0c;对人们的健康和生活品质构成了严重威胁。为了有效监测和改善空气质量&#xff0c;光电离子传感器作为一种先进的检测技术&#xff0c;正在空气质量监测以及HVAC&#xff08;供暖、通风和空调&…

OpenHarmony实战开发-状态变量组件定位工具实践

概述 自定义组件中的变量被状态装饰器&#xff08;State&#xff0c;Prop等&#xff09;装饰后成为状态变量&#xff0c;而状态变量的改变会引起使用该变量的UI组件渲染刷新。状态变量的不合理使用可能会带来冗余刷新等性能问题。开发者可以使用状态变量组件定位工具获取状态管…

JdbcTemplate详解

1 概述 为了使JDBC更加易于使用&#xff0c;Spring在JDBC API上定义了一个抽象层&#xff0c;以此建立一个JDBC存取框架。 作为Spring JDBC框架的核心&#xff0c;JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法&#xff0c;通过这种方式&#xff0c;可以在尽可能保留…

【数据结构(邓俊辉)学习笔记】向量02——动态空间管理

文章目录 1. 概述2. 静态空间管理缺点3. 动态空间管理3.1 扩容3.1.1 如何实现扩容3.1.2 扩容算法3.1.3 容量递增策略 VS 容量倍增策略3.1.3.1 容量倍增策略分摊分析3.1.3.2 容量递增策略分摊分析3.1.3.3 结果对比 3.2缩容3.2.1 动态缩容算法实现3.2.2 动态缩容算法时间复杂度 4…

Sui主网升级至V1.23.1版本

其他升级要点如下所示&#xff1a; #17126 协议&#xff1a;Deepbook的更改将被还原。 #16673 开发者可能会看到更多编译器诊断&#xff0c;因为选择的解析错误不再阻止编译&#xff0c;并且编译器的诊断会到达后续编译阶段&#xff0c;其中可能会生成额外的诊断。 #16966…