【C++】STL学习——priority_queue(了解仿函数)

news2024/9/23 9:28:49

目录

  • priority_queue介绍
  • 迭代器种类
  • priority_queue实现
  • 仿函数
  • 仿函数的使用

priority_queue介绍

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

所谓优先级队列priority_queue,实际上就是我们学过的数据结构——,关于堆的特性及功能可参考——堆的模拟实现一文;而priority_queue跟上文介绍的stack和queue一样,都是容器适配器,都是由其他容器封装而来,但是对底层容器提出了更进一步的要求——底层容器需要支持随机访问迭代器

迭代器种类

  1. 输入迭代器 Input Iterators
  • 提供了对容器中元素的单向访问能力。
  • 支持的操作包括:++(前进)、*(解引用以访问元素)、== 和 !=(比较迭代器是否相等或不等)。
  • 输入迭代器只能向前移动,不能反向移动,也不能直接通过迭代器来修改元素的值(尽管可以通过解引用访问到的元素间接修改,但这通常不是迭代器的职责)。
  • 典型示例:istream_iterator(用于从输入流中读取数据)。
  1. 输出迭代器 Output Iterators
  • 提供了向容器中写入元素的能力。
  • 支持的操作包括:++(前进)、*(解引用以写入元素)。
  • 输出迭代器同样只能向前移动,且不能读取元素的值,主要用于输出操作。
  • 典型示例:ostream_iterator(用于向输出流中写入数据)。
  1. 前向迭代器 Forward Iterators
  • 是输入迭代器的超集,支持所有输入迭代器的操作,并且保证多次通过同一迭代器解引用得到的元素值相同(即迭代器稳定)。
  • 仍然只能向前移动,但比输入迭代器更加灵活,如可以用于accumulate等算法中。
  • 典型示例:forward_list单向链表)的迭代器。
  1. 双向迭代器 Bidirectional Iterators
  • 支持前向迭代器的所有操作,并增加了- -(后退)操作,允许迭代器在容器中前后移动。
  • 典型示例:listmap(注意,map的迭代器是双向的,但只能遍历键值对,不能直接修改键)的迭代器。
  1. 随机访问迭代器 Random Access Iterators
  • 提供了最强大的迭代器能力,支持双向迭代器的所有操作,并且增加了元素间的算术操作,如+n、-n、+=n、-=、<、<=、>、>=。
  • 这类迭代器可以像指针一样,进行随机访问和快速遍历。
  • 典型示例:vectordequestring 的迭代器。

由于priority_queue需要随机访问迭代器,所以一般使用vector,或deque作为底层容器。

priority_queue实现

老样子,封在自己的命名空间里;由于priority_queue是容器适配器,底层也是使用了别的容器(vectordeque)所以实现方法和上篇的stack和queue是一样的,具体请参考stack和queue,而的区别就是priority_queue是堆,需要借助adjust_upadjust_down维持堆的特性。而这两个调整方法也曾在堆模拟实现一文详细介绍过。需要请参考——堆的模拟实现。

我们这里真正需要关心的是:堆有大小堆之分,如何使用同一份代码解决这个问题呢?总不能手动去改代码中的比较逻辑吧。这里的解决办法就是本文将要介绍的——仿函数

namespace djs
{
    ///仿函数
	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 push(const T& x)
		{
			assert(!empty());
			_con.push_back(x);
			//adjust_up(_con.size() - 1);//自己实现的
			push_heap(_con.begin(), _con.end());//使用库实现好的
		}

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			//adjust_down(0);
			pop_heap(_con.begin(),_con.end());//使用库实现好的
		}

		bool empty()
		{
			return _con.empty();
		}

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

	private:
		void adjust_up(int child)
		{
			int parent = (child - 1) / 2;
			Compare com;
			while (child > 0)
			{
				//if (_con[parent] < _con[child])//大小比较
				if (com(_con[parent], _con[child]))//使用仿函数
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void adjust_down(int parent)
		{
			int child = parent * 2 + 1;
			Compare com;
			while (child < _con.size())
			{
				//if (child + 1 < _con.size() && _con[child + 1] > _con[child])//大小比较
				if (child + 1 < _con.size() && (com(_con[child], _con[child + 1])))//使用仿函数
				{
					child++;
				}
				//if (_con[child]>_con[parent])
				if ((com(_con[parent], _con[child])))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

	private:
		Container _con;
	};
	
}

仿函数

仿函数(Functor)是C++中的一个概念,它指的是那些可以被当作函数一样调用的对象。在C++中,这通常是通过重载operator()来实现的。仿函数不仅具有函数的特性(即可以被调用),而且它们还可以拥有状态(即数据成员),这使得它们比普通的函数更加灵活和强大。

堆有分大堆还是小堆,priority_queue通过仿函数解决大小堆的问题;我们看看priority_queue的结构。

priority_queue结构
可以看到第三个参数Compare,它就是所说的仿函数,可以通过传入参数的不同来创建的是大堆还是小堆。

  • 类模板Compare的参数默为less,与之对应的还有greater,使用请包含头文件<functional>
  • less为大堆,greater为小堆;记忆时可以从父节点开始理解,比父节点小的为less,反之。

仿函数的使用

  1. 定义仿函数:首先,你需要定义一个类,并在该类中重载operator()。这个操作符的返回类型和参数列表决定了仿函数使用对象和作用。
  2. 使用仿函数:然后,你可以创建该类的对象,并像调用函数一样调用它(通过()操作符),函数对象是定义了成员函数operator()的类的实例。该成员函数允许以与函数调用相同的语法使用对象。
  3. 仿函数有点类似C语言学习过的回调函数,作为参数参入,等到使用时再调用。

回调函数

回调函数就是⼀个通过函数指针调⽤的函数。如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。

库中的qsort就用到了回调函数。

Compare作为类模板,需要传入一个类,该类因包含对应的仿函数如:Less即建大堆,所以类中的仿函数operator()应该按建大堆的逻辑写。Greater为建小堆,operator()实现的逻辑也是如此。

  • 仿函数指定为operator()的重载,不能是其它。
  • 这里的Less,Greater我们自己模拟写,库里有
  • 现成less和greater,使用时记得包含头文件<functional>
    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
	{
    	void adjust_down(int parent)
		{
			int child = parent * 2 + 1;
			Compare com;
			while (child < _con.size())
			{
				//if (child + 1 < _con.size() && _con[child + 1] > _con[child])
				if (child + 1 < _con.size() && (com(_con[child], _con[child + 1])))
				{
					child++;
				}
				//if (_con[child]>_con[parent])
				if ((com(_con[parent], _con[child])))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
	};

从此我们就可以在使用Less还是Greater指明建的是小堆还是大堆了。

  //小堆     priority_queue<int, vector<int>, Greater<int>> pq1;//与sort不同,sort要传对象,priority_queue传类型就可以,会在用的地方创造对象
 //大堆     	priority_queue<int, vector<int>, Less<int>> pq2;

adjust_down为例:
先创建一个Compare的对象,等需要进行建堆判断时调用该对象。

			Compare com;
			while (child < _con.size())
			{
				//if (child + 1 < _con.size() && _con[child + 1] > _con[child])//直接大小比较
				if (child + 1 < _con.size() && (com(_con[child], _con[child + 1])))//使用仿函数
			}

具体过程可以通过调试观察。

作为STL的六大组件之一:仿函数的功能十分强大,如搭配算法库或功能库的一些函数如sort,按自己的需求重载operator()以达到自己的目的。

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

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

相关文章

Linux 磁盘管理-RAID磁盘冗余阵列看这一篇就够了

今天给伙伴们分享一下Linux 磁盘管理-RAID磁盘冗余阵列&#xff0c;希望看了有所收获。 我是公众号「想吃西红柿」「云原生运维实战派」作者&#xff0c;对云原生运维感兴趣&#xff0c;也保持时刻学习&#xff0c;后续会分享工作中用到的运维技术&#xff0c;在运维的路上得到…

机器学习深度学习

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl1. 深度学习概述 1.1 定义与历史背景 深度学习作为机器学习领域的一个重要分支,其核心在于构建由多层(深层)的人工神经网络组成的计算模型,这些模型能够学习数据的多层次抽象表示。深度学习的…

东大成贤资源库-数据挖掘技术与应用 实验一:数据预处理_熟悉数据挖掘数据预处理流程。 基于给定的数据集(csv)文件,完成下列数据处理。 1

【实验内容】 程序清单 import pandas as pd import numpy as np# 读入文件&#xff0c;存放在字典data里 data pd.read_csv("D:\\Desktop\\data1.csv")# 填入Id列空缺的值 for i in range(1,len(data)):if pd.isnull(data[Id][i]):data[Id][i]i1# Id属性列去重&a…

虚拟机的安装步骤

我这里使用的是VMware 1.下载centos7 2.配置 跟这图来就好 开启虚拟机 第一个页面直接回车,忘了截图 等待安装 选择语言,看自己 点击完成 点击继续安装 设置账号密码 然后等待就行 安装完成之后会有一个重启,点击(又忘了截图) 完成许可和网络 最后就可以了

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代&#xff0c;文本到语音&#xff08;Text-to-Speech, TTS&#xff09;技术已成为人机交互的关键桥梁&#xff0c;无论是为视障人士提供辅助阅读&#xff0c;还是为智能助手注入声音的灵…

数据权限的设计与实现系列7——前端筛选器组件Everright-filter用法说明

背景 官方给了全局性的介绍和示例&#xff0c;不过到了具体使用环节&#xff0c;介绍就不是那么清楚明确了&#xff0c;往往是直接放 demo&#xff0c;需要去推测和揣测&#xff0c;然后动手验证。去百度了下&#xff0c;也没找到现成的对该组件的使用说明&#xff0c;得以开荒…

Unreal Fest 2024 虚幻引擎影视动画制作的普遍问题

一 毛发缓存数据巨大的问题&#xff08;及5个解决方案&#xff09; 在引擎里模拟毛发&#xff0c;并且把它缓存下来&#xff0c;我们就不需要从外部导入了&#xff0c;所以我们要解决的问题就是怎么样在引擎里自由地控制毛发 1.物理场控制 延申 [技术分享]《UE中的世界物理场…

迭代器模式iterator

学习笔记&#xff0c;原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 &#xff08;列表、 栈和树等&#xff09; 的情况下遍历集合中所有的元素

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍&#xff11;、W25Q64简介&am…

P1071 [NOIP2009 提高组] 潜伏者

1.题目太长要耐性总结出题目的要求 2.map中count的使用方法 #include <bits/stdc.h> using namespace std; map<char, char>m0, m1;//m0记录密文对明文&#xff0c;m1记录明文对密文int main() {string x, y, z ;//x为密文&#xff0c;y为明文&#xff0c;z为待翻…

SpringBoot+Redis极简整合

1 前言 Redis是现在最受欢迎的NoSQL数据库之一&#xff0c;下面将以最简洁的代码演示&#xff0c;在SpringBoot中使用redis。 2 下载安装Redis 2.1 下载 Redis3.x windows安装版下载地址 2.2 安装到任意位置 一直Next到完即可。 2.3 启动 打开安装目录&#xff0c;点击…

【数据结构】堆——堆排序与海量TopK问题

目录 前言一、堆排序1.1 整体思路1.2 代码部分1.3 建堆的时间复杂度1.4 堆排序的总结 二、向下调整算法的时间复杂度三、向上调整算法的复杂度四、海量TopK问题4.1 TopK题目 总结 前言 上一篇我们学习了堆的数据结构&#xff0c;现在我们来看看堆的日常应用和排序 一、堆排序 …

内网安全-横向移动【3】

1.域横向移动-内网服务-Exchange探针 Exchange是一个电子右键服务组件&#xff0c;由微软公司开发。它不仅是一个邮件系统&#xff0c;还是一个消息与协作系统。Exchange可以用来构建企业、学校的邮件系统&#xff0c;同时也是一个协作平台&#xff0c;可以基于此开发工作流、…

用广播星历计算卫星运动的平均角速度

用广播星历计算卫星位置 1.计算卫星运动的平均角速度 首先根据广播星历中给出的参数计算参考时刻的平均角速度: 式中&#xff0c;GM为万有引力常数G与地球总质量M之乘积&#xff0c;其值为GM3.98600510^14b m3/s2。 然后根据广播星历中给定的摄动参数计算观测时刻卫星的平均…

模版方法模式template method

学习笔记&#xff0c;原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架&#xff0c; 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresearch/segment-…

C++-----STL简介(了解)

1. 什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2. STL的版本 原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版…

手撕Python之生成器、装饰器、异常

1.生成器 生成器的定义方式&#xff1a;在函数中使用yield yield值&#xff1a;将值返回到调用处 我们需要使用next()进行获取yield的返回值 yield的使用以及生成器函数的返回的接收next() def test():yield 1,2,3ttest() print(t) #<generator object test at 0x01B77…

MATLAB-基于高斯过程回归GPR的数据回归预测

目录 目录 1 介绍 1. 1 高斯过程的基本概念 1.2 核函数&#xff08;协方差函数&#xff09; 1.3 GPR 的优点 1.4. GPR 的局限 2 运行结果 3 核心代码 1 介绍 高斯过程回归&#xff08;Gaussian Process Regression, GPR&#xff09;是一种强大的非参数贝叶斯方法&…

JAVA- 多线程

一&#xff0c;多线程的概念 1.并行与并发 并行&#xff1a;多个任务在同一时刻在cpu 上同时执行并发&#xff1a;多个任务在同一时刻在cpu 上交替执行 2.进程与线程 进程&#xff1a;就是操作系统中正在运行的一个应用程序。所以进程也就是“正在进行的程序”。&#xff0…