[ C++ ] STL---仿函数与priority_queue

目录

仿函数

示例一:

示例二 :

常见的仿函数

priority_queue简介

priority_queue的常用接口

priority_queue的模拟实现

基础接口

push()

堆的向上调整算法

堆的插入

pop()

堆的向下调整算法

堆的删除

priority_queue最终实现


仿函数

仿函数(Functor)是一个类/结构体,其内部重载了operator()运算符,使其可以像函数一样被调用;

  1. 由于模版参数接收类型,仿函数是一个类,可以通过 类名/类名<数据类型> 传递给模版参数
  2. 仿函数是一个类,则其可以定义对象对象调用operator()完成控制作用;

示例一:

//带模版参数的仿函数
template<class T>
struct Greater
{//重载operator()bool operator()(const T& x, const T& y){return x > y;}
};
void Func()
{int x = 10;int y = 20;//创建一个仿函数对象Greater<int> Great;//通过对象调用仿函数cout << Great(x, y) << endl;//cout<<Great.operator()(x,y)<<endl;//通过匿名对象调用仿函数cout << Greater<int>()(x, y) << endl;
}
int main()
{Func();return 0;
}

示例二 :

//此模版参数接收数据类型
template<class T>
struct Greater
{//重载operator()称为仿函数/函数对象bool operator()(const T& x, const T& y){return x > y;}
};
//此模版参数接收仿函数类型
template<class compare>
class A
{
public:void func(int a, int b){compare com;cout << com(a, b) << endl;//com(a,b)--->com.operator()(a,b)--->com为仿函数对象}
};int main()
{//A类中传递仿函数类型Greater<T>-->仿函数类中传递数据类型Greater<int>A<Greater<int>> aa1;aa1.func(10, 20);A<Greater<int>>aa2;aa2.func(20, 10);return 0;
}

具有相同功能的代码可以在不同的类中用到,但是不好将功能相同的代码实现成某一个类的成员函数,仿函数实现了一个简单的类,将需要复用的代码实现在operator()重载函数中,外部函数需要使用时只要用这个类实例化出一个对象,就可以像使用函数一样来使用这个对象,完成对应功能;

常见的仿函数

template <class T> 
struct less 
{bool operator() (const T& x, const T& y) const {return x<y;}
};
//std::less<T>: 对于基本数据类型和自定义类型,默认使用<运算符进行比较;

template <class T> 
struct greater 
{bool operator() (const T& x, const T& y) const {return x>y;}
};
//std::greater<T>: 对于基本数据类型和自定义类型,默认使用>运算符进行比较;

除了c++标准库所提供的仿函数外,可以自定义仿函数来实现自定义的元素比较规则,自定义仿函数需要满足严格弱排序的要求,即:

  • 比较关系必须是可传递的:对于任意元素a、b和c,如果a与b比较相等,b与c比较相等,则a与c比较也相等;
  • 比较关系不能是部分顺序:对于任意元素a和b,它们不能同时大于、小于或等于彼此;
  • 比较关系必须是可比较的:比较关系的结果必须对所有元素定义明确的大小关系;

priority_queue简介

1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的;

2. 优先队列的底层数据结构为堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素);

3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素,元素从特定容器的"尾部"弹出,其称为优先队列的顶部

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

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

5. 标准容器类vector和deque满足这些需求;默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector

6. 需要支持随机访问迭代器,以便始终在内部保持堆结构;容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作;

注意:

  1. 由于优先队列底层数据结构为堆,堆的删除是删除堆顶数据,首先将堆顶数据与最后一个数据交换位置,其次删除数组中最后一个元素,所以容器中要求pop_back();
  2. 由于优先队列底层数据结构为堆,堆的插入是数组的尾元素的下一个位置插入数据,然后利用向上调整算法调整为堆,所以容器中要求push_back();
  3. 默认情况下,priority_queue使用std::less作为比较函数创建的是大堆;如果需要按照从小到大的顺序排列,可以使用std::greater作为比较函数,创建的是小堆

堆的相关知识点回顾:CSDN

priority_queue官方文档:priority_queue - C++ Reference

priority_queue的常用接口

int main()
{//未给定仿函数比较方式,默认创建大根堆priority_queue<int, vector<int>> pq;//尾插数据pq.push(1);pq.push(2);pq.push(6);pq.push(2);pq.push(3);cout << "size=" << pq.size() << endl;while (!pq.empty()){//取堆顶数据cout << pq.top() << " ";//删除堆顶元素pq.pop();}cout << endl;return 0;
}

运行结果:

int main()
{//给定比较方式为greater,创建小根堆priority_queue<int, vector<int>,greater<int>> pq;//尾插数据pq.push(1);pq.push(2);pq.push(6);pq.push(2);pq.push(3);cout << "size=" << pq.size() << endl;while (!pq.empty()){//取堆顶数据cout << pq.top() << " ";//删除堆顶元素pq.pop();}cout << endl;return 0;
}

运行结果:

priority_queue的模拟实现

基础接口

#include <iostream>
#include <vector>
template<class T, class Container = vector<T>>
class priority_queue
{
public://获取堆顶元素即数组首元素const T& top(){return _con[0];}//获取堆的数据个数即容器中的数据个数size_t size(){return _con.size();}//检测堆是否为空即容器是否为空bool empty(){return _con.empty();}
private:Container _con;
};

push()

堆的存储结构为数组,尾插时间复杂度O(1),首先将元素插入到数组的尾部,其次利用堆的向上调整算法,将数组调整为大根堆/小根堆;

堆的向上调整算法

case 1:

当插入的数值M大于其父节点的值时,仍然为小堆,此时不做任何调整;

case 2:

当插入的数值M小于其父节点的值时,破坏了堆的逻辑结构,向上调整为小堆;

(假设M=5)

向上调整的过程中,只要出现待调整的孩子结点大于其父节点,便可停止调整,已经满足小堆的逻辑结构;

//向上调整为小堆
void adjustup(size_t child)
{size_t parent = (child - 1) / 2;while (child > 0){if (_con[child] < _con[parent]){//交换swap(_con[child], _con[parent]);//向上走child = parent;parent = (child - 1) / 2;}else{break;}}
}
//向上调整为大堆
void adjustup(size_t child)
{size_t parent = (child - 1) / 2;while (child > 0){if (_con[child] > _con[parent]){//交换swap(_con[child], _con[parent]);//向上走child = parent;parent = (child - 1) / 2;}else{break;}}
}

堆的插入

void push(const T& val)
{//插入数据_con.push_back(val);//从插入值为val的孩子结点的下标开始向上调整为小堆/大堆adjustup(_con.size()-1);
}

pop()

堆的删除是删除堆顶数据,首先将堆顶元素与堆中最后一个元素交换,其次删除堆中最后一个元素,最后将堆顶元素利用向下调整算法调整到满足其逻辑结构为堆;

注:堆的向下调整算法的前提左右子树同为小堆/同为大堆;

堆的向下调整算法

case 1:左右子树皆为小堆,向下调整为小堆;

//向下调整为小堆
void adjustdown(size_t parent)
{size_t child = 2 * parent + 1;while (child < _con.size()){//假设法求同一层孩子节点最小者if (child + 1 < _con.size() && _con[child] > _con[child + 1]){child++;}//逻辑关系不满足小堆,交换调整if (_con[child] < _con[parent]){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}else{break;}}
}

case 2:左右子树皆为大堆,向下调整为大堆;

//向下调整为大堆
void adjustdown(size_t parent)
{size_t child = 2 * parent + 1;while (child < _con.size()){//假设法求同一层孩子节点最大者if (child + 1 < _con.size() && _con[child] < _con[child + 1]){child++;}//逻辑关系不满足大堆,交换调整if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}else{break;}}
}

堆的删除

void pop()
{//将堆顶元素与堆中最后一个元素交换swap(_con[0], _con[size() - 1]);//删除最后一个元素_con.pop_back();//将堆顶元素利用向下调整算法调整到满足其逻辑结构为堆adjustdown(0);
}

priority_queue最终实现

堆分为大根堆与小根堆,向上调整算法/向下调整算法对于大根堆/小根堆的实现只有各别符号的差别,外部使用优先队列(priority_queue)时,只能在内部手动修改相应的符号来控制生成的是大堆/小堆,对于使用者而言,只能创建大堆/小堆,两者只能实现一个,故引入第三个模版参数传递仿函数类型,指定比较方式,此时使用者只需给定比较方式便可在大小堆之间自由切换;

注意:由于容器中一开始没有数据,故不用建堆,它是每插入一个数据,调用向上调整法,删除数据,调用向下调整法,不断地插入和删除,并且使其保持堆的逻辑结构 ;

#include <iostream>
#include <vector>
#include <functional>
using namespace std;
template<class T, class Container =vector<T>, class Compare = less<T>>
class priority_queue
{
public:bool empty() const{return _con.empty();}size_t size() const{return _con.size();}const T& top() const{return _con[0];}T& top(){return _con[0];}//向上调整算法void adjustup(size_t child){size_t parent = (child - 1) / 2;while (child > 0){//_con[parent]<_con[child],父节点小于其孩子节点,交换向上调整--->大堆if (_cmp(_con[parent], _con[child])){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}void push(const T& val){_con.push_back(val);adjustup(_con.size() - 1);}//向下调整算法void adjustdown(size_t parent){size_t child = parent * 2 + 1;while (child <_con.size()){//child+1< _con.size()保证右孩子结点存在//less<T> _cmp ---> _con[child]<_con[child+1]--->child++ --->挑选大的孩子结点if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1])){child++;}//_con[parent]<_con[child],父节点小于其孩子结点,交换向下调整---->大堆if (_cmp(_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(0);}
private:Container _con;Compare _cmp;//less<T> _cmp;
};

欢迎大家批评指正,博主会持续输出优质内容,谢谢大家观看,码字画图不易,希望大家给个一键三连支持~ 你的支持是我创作的不竭动力~

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

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

相关文章

MusicHiFi: Fast High-Fidelity Stereo Vocoding

MusicHiFi: Fast High-Fidelity Stereo Vocoding 相关链接&#xff1a;arxiv github 关键字&#xff1a;音乐生成、高保真立体声、立体声编解码器、生成对抗网络、频带扩展 摘要 MusicHiFi是一种高效的高保真立体声编解码器&#xff0c;它通过将低分辨率的mel频谱图转换为音频…

【Vue】Vue集成Element-UI框架

&#x1f64b;‍ 一日之际在于晨 ⭐本期内容&#xff1a;Vue集成Element-UI框架 &#x1f3c6;系列专栏&#xff1a;从0开始的Vue之旅 文章目录 Element-UI简介安装Element-UInpm安装CDN安装 引入Element-UI测试是否引入成功总结 Element-UI简介 Element-UI官网&#xff1a;点…

极大提高工作效率的 Linux 命令

作为一名软件开发人员&#xff0c;掌握 Linux 命令是必不可少的技能。即使你使用 Windows 或 macOS&#xff0c;你总会遇到需要使用 Linux 命令的场合。例如&#xff0c;大多数 Docker 镜像都基于 Linux 系统。要进行 DevOps 工作&#xff0c;你需要熟悉Linux&#xff0c;至少要…

vue-quill-editor和vue-ueditor-wrap富文本编辑器应用

目录 一、vue-quill-editor 1.1、界面展示 1.2、代码介绍 1.2.1、安装 1.2.2、配置 1.2.3、代码应用 1.2.4、提取内容 二、vue-ueditor-wrap 2.1、界面展示 2.2、代码介绍 2.2.1、安装 2.2.2、配置 2.2.3、代码应用 一、vue-quill-editor 1.1、界面展示 文本输出…

Vue响应式原理全解析

前言 大家好&#xff0c;我是程序员蒿里行。浅浅记录一下面试中的高频问题&#xff0c;请你谈一下Vue响应式原理。 必备前置知识&#xff0c;​​Vue2​​官方文档中​​深入响应式原理​​​及​​Vue3​​官方文档中​​深入响应式系统​​。 什么是响应式 响应式本质是当…

liunx CentOS7 搭建lnmp环境 php nginx mysql

安装一些刚需软件&#xff1a;不懂请自行查询 安装一些需要的软件命令 yum install wget vim net-tools bash* lrzsz tree nmapnc lsof telnet -y 刷新命令 source /usr/share/bash-completion/bash_completion echo source /usr/share/bash-completion/bash_completion &…

UE5 C++ 3D血条 响应人物受伤 案例

一.3Dwidget 1.创建C Userwidget的 MyHealthWidget&#xff0c;声明当前血量和最大血量 UCLASS() class PRACTICEC_API UMyHealthWidget : public UUserWidget {GENERATED_BODY() public:UPROPERTY(EditAnywhere,BlueprintReadWrite,Category "MyWidget")float C…

利用API打造卓越的用户体验

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 正文 1. 数据驱动的设计 2. 功能扩展与整合 3. 实时性与响应性 4. 个性化推荐与定制化服务 结语 我的其他博客 正文 随着数字化时代的…

npm i安装依赖报错,但是cnpm i 却安装成功

问题描述&#xff1a;在a项目中npm i 安装依赖时发生以上报错&#xff0c;但是cnpm i 却成功&#xff0c;而且在其他项目中npm i 安装其他项目依赖也能成功.... 解决办法&#xff1a;删除项目中package-lock.json文件后再npm i 即可

搭建Linux内核开发环境——保姆教程(持续更新中)

搭建Linux内核开发环境——保姆教程&#xff08;持续更新中&#xff09; git版本管理汇编器链接器调试器编辑器构建系统模拟器文档工具图形设计工具 在此文中&#xff0c;持续完善&#xff0c;搭建内核开发环境的细节&#xff0c;有需要的小伙伴儿可以持续关注下 git版本管理 …

Ruby选择结构实战

文章目录 一、Ruby选择结构实战概述二、Ruby选择结构实战案例&#xff08;一&#xff09;闰年判断1、编写程序&#xff0c;实现功能2、程序的解释说明3、运行程序&#xff0c;查看结果 &#xff08;二&#xff09;求解一元二次方程1、编写程序&#xff0c;实现功能2、程序的解释…

Linux--Flappy_bird实现

目录 void handler(int sig): mian: void init_curses() int set_timer(int ms_t); 小鸟的操作&#xff1a; void show_pipe()&#xff1a; void create_list() void clear_pipe() void move_pipe(); test_bird.c完整代码&#xff1a; 代码实现&#xff1a; #includ…

STM32 CubeMX使用介绍(含FreeRTOS生成)

文章目录 前言1. 简介1.1 什么是STM32CubeMX1.2 为什么会有STM32CubeMX的出现1.3 STM32CubeMX常用功能有哪些&#xff1f;1.4 官方资料下载地址 2. 下载和安装STM32CubeMX2.1 下载软件2.2 软件安装 3. 使用方式3.1 说明3.2 不同选择器介绍3.3 构建新的项目3.1 选择单片机的型号…

题目:出列(蓝桥OJ 3223)

问题描述&#xff1a; 解题思路&#xff1a; 先使用暴力找到规律再解。 暴力做法&#xff1a;将数据放到一个动态数组中&#xff0c;下标就表示当前编号&#xff0c;符合题意的就放到覆盖该数组中&#xff0c;依次循环&#xff0c;直到只有一个元素停。 规律&#xff1a;小于该…

爬虫(七)

1.批量爬取知网数据 lxml:是 Python 的一个功能强大且易用的 XML 和 HTML 处理库。它提供了简单又轻巧的 API,使得解析、构建和操作 XML 和 HTML 文档变得非常方便。lxml 库通常用于处理 XML 和 HTML 文档,例如解析网页、处理配置文件等。openpyxl:是 Python 中用于操作 Ex…

uniapp自定义导航栏左中右内容和图标,以及点击事件

uniapp自定义导航栏左中右内容和图标&#xff0c;以及点击事件 效果&#xff1a; 页面&#xff1a; <view class"navigation-bar"><view class"navigation-bar-left" click"navigateBack"><u-icon name"arrow-left"…

【嵌入式开发 Linux 常用命令系列 4.3 -- git add 时单独排除某个目录或者文件】

文章目录 git add 时单独排除某个目录或者文件使用 .gitignore 文件使用命令行排除文件或目录 git add 时单独排除某个目录或者文件 在使用 git add 命令时&#xff0c;如果你想要排除特定的目录或文件&#xff0c;可以使用 .gitignore 文件或使用路径规范来指定不想添加的文件…

新的变速箱滚动轴承和齿轮故障数据

变速箱是传动系统中非常关键的一部分&#xff0c;它由齿轮、传动轴、轴承和壳体等组成。变速箱的主要功用包括&#xff1a;&#xff08;1&#xff09;能够改变传动比&#xff0c;按实际情况调整驱动轮转矩和转速&#xff0c;进而满足复杂的行车要求&#xff1b;&#xff08;2&a…

机器学习金融应用技术指南

1 范围 本文件提供了金融业开展机器学习应用涉及的体系框架、计算资源、数据资源、机器学习引擎、机 器学习服务、安全管理、内控管理等方面的建议。 本文件适用于开展机器学习金融应用的金融机构、技术服务商、第三方安全评估机构等。 2 规范性引用文件 下列文件中的内容通过…

新型储能是什么,储能系统解决方案现状及趋势详细说明

新型储能是指新兴的能够存储电能并在需要时释放的储能技术。其中主要包括光伏储能和商业储能。 光伏储能是指通过光伏电池将太阳能转化为电能&#xff0c;并将其存储起来以供后续使用。光伏储能系统一般由太阳能电池板、储能装置和逆变器组成。光伏储能可以将白天产生的电能存…