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

目录

  • 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()以达到自己的目的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL 编程基础

SQL&#xff08;结构化查询语言&#xff09;广泛应用于数据库操作&#xff0c;是每个程序员都需要掌握的技能之一。这篇文章将带你从基础入门&#xff0c;了解SQL编程中的常量、变量及流程控制语句。我们将采用简单易懂的语言&#xff0c;结合实际示例&#xff0c;帮助你轻松理…

uniapp scroll-view滚动页面

页面滚动固定距离&#xff08;scrollTop&#xff09; <template><view><button click"Test">测试</button><scroll-view style"height: 100px;" :scroll-top"scrollTop" scroll-y"true" class"scrol…

7系列FPGA HR/HP I/O区别

HR High Range I/O with support for I/O voltage from 1.2V to 3.3V. HP High Performance I/O with support for I/O voltage from 1.2V to 1.8V. UG865&#xff1a;Zynq-7000 All Programmable SoC Packaging and Pinout

基于tesseract实现文档OCR识别

导入环境 导入必要的库 numpy: 用于处理数值计算。 argparse: 用于处理命令行参数。 cv2: OpenCV库&#xff0c;用于图像处理。 import numpy as np import argparse import cv2设置命令行参数 ap argparse.ArgumentParser() ap.add_argument("-i", "--imag…

Codeforces Round 970 (Div. 3)(ABCDEF)

Codeforces Round 970 (Div. 3) A:Sakurakos Exams 签到 题意:给定1,2的数量,判断是否能用加减符号使得这些1,2计算出0 void solve() {cin>>n>>m;if(n%2)cout<<"NO\n";else{if(m%20||n)cout<<"YES\n";else cout<<"…

如何在docker容器中导入.sql文件

一、准备工作 确保容器运行&#xff1a; 首先确认包含 MySQL 服务的 Docker 容器正在运行。可以通过 docker ps 命令查看正在运行的容器列表。如果容器未运行&#xff0c;使用 docker start [container_id] 命令启动容器。 准备数据库文件&#xff1a; 将需要导入的数据库文件&…

用户画像的人群圈选

背景 用户画像的人群圈选一般涉及到bitmap的运算&#xff0c;我们可以使用java中的RoaringBitmap进行运算&#xff0c;也可以直接使用ck的bitmap进行运算&#xff0c;本文就来看一下这两种方式 人群圈选 使用java的RoaringBitmap进行运算&#xff1a; 使用clickhouse的bit…

145-Linux权限维持Rootkit后门Strace监控Alias别名Cron定时任务

参考 【权限维持】Linux&Rootkit后门&Strace监控&Alias别名&Cron定时任务_alias lsalerts(){ ls $* --colorauto;python -c "-CSDN博客 参考 FlowUs 息流 - 新一代生产力工具 权限维持-Linux-定时任务-Cron后门 利用系统的定时任务功能进行反弹Shell 1…

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物&#xff0c;今天我们实现一下人物实现移动和跳起&#xff0c;依次点击&#xff0c;我们准备创建一个C#文件 创建好我们点击进去&#xff0c;就会跳转到我们的Vision Studio&#xff0c;然后输入这些代码 using UnityEngine;public class M…

java基础概念21-权限修饰符、代码块

一、权限修饰符 1-1、作用 权限修饰符&#xff0c;是用来控制一个成员能够被访问的范围的。 可以修饰&#xff1a;成员变量&#xff0c;方法&#xff0c;构造方法&#xff0c;内部类。 1-2、权限修饰符的分类 二、代码块 局部代码块构造代码块静态代码块 2-1、局部代码块 …

IM即时通讯,稳定可靠的即时通讯服务-WorkPlus

在现代企业日常工作中&#xff0c;即时通讯已成为了一种不可或缺的沟通工具。为了满足企业对稳定可靠的即时通讯服务的需求&#xff0c;WorkPlus提供了一款优秀的IM即时通讯平台&#xff0c;以满足企业高效沟通和协作的要求。本文将深入探讨IM即时通讯服务的重要性以及WorkPlus…

navigator.mediaDevices.getUserMedia检查用户的摄像头是否可用,虚拟摄像头问题

在Web开发中&#xff0c;检查用户的摄像头是否可用是一个常见的需求&#xff0c;尤其是在需要视频聊天或录制视频的应用程序中。navigator.mediaDevices.getUserMedia() API 提供了这一功能&#xff0c;它允许你请求访问用户的媒体设备&#xff0c;如摄像头和麦克风。虽然这个A…

数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树

文章目录 树与二叉树的应用1.哈夫曼树与哈夫曼曼编码1.1 带权路径长度1.2 哈夫曼树1.2.1 哈夫曼树的构造1.3 哈夫曼编码 2.并查集2.1 并查集的三要素2.1.1 并查集的逻辑结构2.1.2 并查集的存储结构 2.2 并查集的优化2.2.1 初步优化&#xff08;并操作优化&#xff09;2.2.2 终极…

【优选算法】---前缀和

前缀和 一、【模板】一维前缀和二、【模板】二维前缀和三、寻找数组的中心下标四、除自身以外数组的乘积五、和为K子数组六、和可被 K 整除的子数组七、连续数组八、矩阵区域和 一、【模板】一维前缀和 一维前缀和&#xff0c;链接 1、预处理出来一个前缀和数组 注意&#xf…

消息中间件 --Kafka

一、 Kafka 1.kafka介绍 Kafka 是一个分布式流媒体平台,类似于消息队列或企业消息传递系统。 生产者发送消息&#xff0c;多个消费者只能有一个消费者接收到消息 生产者发送消息&#xff0c;多个消费者都可以接收到消息 producer&#xff1a;发布消息的对象称之为主题生产者…

时序数据库 IoTDB 为什么选择 TPCx-IoT 基准测评?

IoTDB 在 TPCx-IoT 榜单的 What 与 Why 解答&#xff01; 去年&#xff0c;我们发布了 IoTDB 多项性能表现位居国际数据库性能测试排行榜 benchANT&#xff08;Time Series: DevOps&#xff09;第一名的好消息。 刚刚落幕的数据库顶级会议 VLDB 上&#xff0c;我们又收获了一则…

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点&#xff1a; 鼠标左键双击&#xff0c;设定红色的起点。左键双击设定起点&#xff0c;用红色标记。 设定终点&#xff1a; 鼠标右键双击&#xf…

猴王采集——多多采集必备,关键词,类目整店采集,正在拼爆款截流采集

在日新月异的电商领域&#xff0c;数据就是商战的情报&#xff0c;而精准、高效的数据采集则成为商家们制胜的关键。今天&#xff0c;让我们一同探索一款颠覆传统、引领未来的数据采集工具 一、关键词类目整店采集&#xff1a; “猴王采集”凭借其强大的关键词搜索能力&#…

什么是 TDengine?

TDengine 是一款专为物联网、工业互联网等场景设计并优化的大数据平台&#xff0c;其核心模块是高性能、集群开源、云原生、极简的时序数据库。它能安全高效地将大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据进行汇聚、存储、分析和分发&#xff0c;对业务运行状态进…

深入RabbitMQ世界:探索3种队列、4种交换机、7大工作模式及常见概念

文章目录 文章导图RabbitMQ架构及相关概念四大核心概念名词解读 七大工作模式及四大交换机类型0、前置了解-默认交换机DirectExchange1、简单模式(Simple Queue)-默认DirectExchange2、 工作队列模式(Work Queues)-默认DirectExchange3、发布/订阅模式(Publish/Subscribe)-Fano…