【C++】仿函数和priority_queue(优先级队列)

目录

一、仿函数 

二、priority_queue(优先级队列)

1、概念:

2、使用:

3、数组中第K个最大元素

4、priority_queue的模拟实现


一、仿函数 

①、概念:

仿函数,即函数对象。一种行为类似函数的对象,调用者可以像函数一样使用该对象,其实现起来也比较简单:用户只需实现一种新类型,在类中重载operator()即可,参数根据用户所要进行的操作选择匹配。

②、代码:

  •  用内置类型比较大小关系:
//仿函数/函数对象 --- 对象可以像调用函数一样去使用
struct less
{//()运算符重载--用于比较大小bool operator()(int x, int y){return x < y;}
};
  • 利用模板比较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;}
};

 ③、测试: 

//测试less
less<int> LessFunc;
cout << LessFunc(1, 2) << endl;//1
//测试greater
greater<int> GreaterFunc;
cout << GreaterFunc(1, 5) << endl;//0

为什么说仿函数又叫函数对象?

比如测试代码中的LessFunc,它是个对象,但他调用时直接写为LessFunc(1, 2),就像一个函数在调用,但他并不是函数,调用的本质是lessFunc.operator()(1, 2),所以仿函数即对象可以像函数一样使用

④、algorithm中的sort

 sort的第三个参数使用到了仿函数,其为仿函数的对象

第一个参数和第二个参数都是迭代器,第三个参数是仿函数,其默认值为less

升序:less <       降序:greater >  

注:第三个参数不传默认是less,即排升序

#include<iostream>
#include<algorithm>
using namespace std;void test_sort()
{vector<int>v;v.push_back(1);v.push_back(4);v.push_back(5);v.push_back(2);//升序,less <sort(v.begin(), v.end());for (auto e : v){cout << e << " ";}cout << endl;//降序 greater >// 写法一、定义一个对象 //greater<int> gt;//sort(v.begin(), v.end(),gt);//写法二、匿名对象(更推荐)sort(v.begin(), v.end(), greater<int>());//greater<int>()是匿名对象for (auto e : v){cout << e << " ";}cout << endl;}int main()
{//test_priority_queue();test_sort();return 0;
}


二、priority_queue(优先级队列)

1、概念:

 优先级队列queue不一样,它是优先级高的先走(默认情况是大的数优先级高,但如果想要小的优先级高,如何操作?-> 用仿函数),它的底层其实是中的大堆,不用数组的原因是堆的效率高

2、使用:

 queue的头文件同时包含了priority_queue和queue,所以用priority_queue直接#include<queue>即可,

注:容器适配器都不支持迭代器遍历,因为他们通常都包含一些特殊性质,如果支持迭代器随便遍历,那他们无法很好的保持他的性质,这里priority_queue也是容器适配器,故不支持迭代器

由运行结果可知,默认情况下是大的数优先级高,若要使小的优先级高,需要调用仿函数,priority_queue的第一个参数是值的类型,第二个参数是内部的适配容器,第三个参数是仿函数,而仿函数要引头文件 #include<functional>(仿函数下文会讲)

下面给出使小的优先级高的代码:

3、数组中第K个最大元素

基本介绍后我们来做一道可用priority_queue实现的题(三种解法)

 法一、优先级队列实现

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int> pq;for (auto e : nums)pq.push(e);//把所有数据插入到pq中while (--k)pq.pop();//执行了k-1次的删除return pq.top();//剩下的一个元素就是第k大的}
};

时间复杂度:O(N*logN)

解释:优先级队列建堆:O(N),插入数据push:O(N*logN),删除数据pop:O(k*logN),三者相加,综上时间复杂度为O(N*logN)

空间复杂度:O(N)

因为开辟的优先级队列的空间,故为O(N)

法二、用算法中的sort实现

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {//解法二:sort(nums.begin(), nums.end());//不用仿函数的话默认情况下排升序return nums[nums.size() - k];//返回倒数第二个即可}
};

时间复杂度:O(N*logN)

因为sort的底层是快排,快排的时间复杂度:O(N*logN)

空间复杂度:O(1)

现假设N是一千万,K是100,这时时间空间消耗都很大,怎么优化?建堆实现

法三、用堆实现(类似于TopK问题)

这个其实跟TopK问题差不多,只不过这里是找第k个大的那个,故建有k个数的小堆(那么这k个数最后都会变成前k大的数),只要比堆顶大,就能进入堆中

下面是我之前写过的TopK求解思路(推荐看,便于理解):

【数据结构】---TopK问题_姜暮、的博客-CSDN博客

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {//解法三://利用仿函数建小堆,因为默认情况下优先级队列是建大堆priority_queue<int, vector<int>, greater<int>>minHeap;size_t i = 0;for (; i < k; ++i)minHeap.push(nums[i]);//先入k个数据(数据是什么无所谓)//使堆中是前k大的数据for (; i < nums.size(); ++i){if (nums[i] > minHeap.top()){//比堆顶大的就替换堆顶minHeap.pop();minHeap.push(nums[i]);}}return minHeap.top();//因为是小堆,则堆顶即k个数中它是最小的,因为k个数是前k大的}
};

时间复杂度:O(N*logK)

解释:优先级队列建堆:O(N),k个数据的push:O(logk),因为本质是向上调整法≈k次,比较的过程:O(N*2*logk)三者相加,综上为O(N*logk) 

空间复杂度:O(K)

当N很大的时候,这个方法的效率是非常好的

4、priority_queue的模拟实现

向上调整法和向下调整法为什么需要用到仿函数?

因为堆分大堆小堆,向上和向下调整法对于大小堆的实现只有一个符号的差别,难道写两份调整法?不够好,故用仿函数实现,使大堆和小堆都能用一份代码

若参数是less,会建大堆,排升序
若参数是greater,会建小堆,排降序

为什么无需数组建堆?

之前讲的是已经存在数据的数组,他现有的顺序很可能不符合大堆或小堆的性质,故要数组建堆,这样插入删除等操作才能很好进行,而这里的模拟实现,一开始就没有数据,故不用建堆,它是每插入一个数据,调用向上调整法,删除数据,调用向下调整法,不断地插入和删除,还能使其保持堆的性质 

 priority_queue.h:

#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
#include<queue>
#include<functional>
#include<algorithm>namespace mz
{//仿函数/函数对象 --- 对象可以像调用函数一样去使用template<class T>struct less{//()运算符重载--用于比较大小bool operator()(const T& x, const T& y) const{return x < y;}};template<class T>struct greater{//()运算符重载--用于比较大小bool operator()(const T& x, const T& y) const{return x > y;}};//优先级队列template<class T, class Container = vector<T>, class Compare = less<T>>class priority_queue{public://向上调整算法void AdjustUp(int child){Compare com;//创建一个仿函数对象int parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child]))//利用仿函数建大堆或小堆{swap(_con[parent], _con[child]);//更新child和parentchild = parent;parent = (child - 1) / 2;}else{//此时不需要调整,直接breakbreak;}}}//向下调整算法void AdjustDown(int root){int parent = root;int child = parent * 2 + 1;Compare com;//仿函数while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child],_con[child + 1])){//利用仿函数,区别大小堆向下调整法的不同之处child++;}if (com(_con[parent], _con[child])){swap(_con[parent], _con[child]);//更新child和parentparent = child;child = parent * 2 + 1;}else{//此时不需要调整,直接breakbreak;}}}//插入数据void push(const T& x){_con.push_back(x);//每插入一个数据,都要向上调整建堆AdjustUp((int)_con.size() - 1);}//删除数据void pop(){assert(!_con.empty());//删除的前提:不为空swap(_con[0], _con[_con.size() - 1]);//交换头尾数据_con.pop_back();//删除最后一个数据AdjustDown(0);//从根部向下调整建堆}//取堆顶数据const T& top(){return _con[0];}//获取size有效数据个数size_t size(){return _con.size();}//判空bool empty(){return _con.empty();}private:Container _con;};
}

 test.cpp:

using namespace std;
#include"priority_queue.h"void test_priority_queue()
{//priority_queue<int>pq;//默认大的优先级高mz::priority_queue<int, vector<int>, greater<int>> pq;//变成小的优先级高pq.push(3);pq.push(1);pq.push(9);pq.push(4);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;}int main()
{test_priority_queue();//test_sort();return 0;
}

运行结果:

 

 STL的大体总结:

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

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

相关文章

Java+Tif图片转Jpg

Tif转Jpg使用心得&#xff1a; 如果tif图片需要压缩&#xff0c;或者需要做转换&#xff0c;常用方法&#xff1a; File file1 new File("E:\\www\\ffw\\images\\73.jpg");byte[] bigContent Files.readAllBytes(file1.toPath());ByteArrayInputStream byteArrayIn…

解决Maven依赖下载问题:从阿里云公共仓库入手

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

安装RabbitMQ的各种问题(包括已注册成windows服务后,再次重新安装,删除服务重新注册遇到的问题)

一、安装Erlang&#xff08;傻瓜式安装&#xff09; 安装完成之后&#xff0c;配置环境变量&#xff1a; 1.新建系统变量名为&#xff1a;ERLANG_HOME 变量值为erlang安装地址 2. 双击系统变量path&#xff0c;点击“新建”&#xff0c;将%ERLANG_HOME%\bin加入到path中。 …

c++类与对象(中)

文章目录 前言一、构造函数1、构造函数介绍2、构造函数特性 二、析构函数1、析构函数介绍2、析构函数特性 三、拷贝构造函数1、拷贝构造函数介绍2、拷贝构造函数特征3、拷贝构造函数的应用 -- 求n天后的日期 四、赋值运算符重载1、运算符重载2、一些运算符重载的实现3、赋值运算…

C++数据结构X篇_12_树的基本概念和存储

学习二叉树之前先学习树的概念。 文章目录 1. 树的基本概念1.1 树的定义1.2 树的特点1.3 若干术语 2. 树的表示法2.1 图形表示法2.2 广义表表示法 3. 树的存储3.1 双亲表示法&#xff1a;保存父节点关系3.2 孩子表示法3.3 左孩子右兄弟表示法 1. 树的基本概念 之前所学均为线性…

前端构建工具 webpack 笔记

1、了解 webpack 1、定义&#xff1a;本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具&#xff0c;当 webpack 处理应用它会在内部从一个或多个入口点构建一个依赖图(dependency graph)&#xff0c;然后将你项目中所程序时&#xff0c;需的…

【javaSE】 反射与反射的使用

文章目录 &#x1f332;反射的定义&#x1f38d;反射的用途&#x1f334;反射基本信息&#x1f340;反射相关的类&#x1f6a9;Class类(反射机制的起源 )&#x1f388;Class类中的相关方法 &#x1f6a9;反射示例&#x1f388;获得Class对象的三种方式&#x1f388;反射的使用 …

Activating More Pixels in Image Super-Resolution Transformer(HAT)超分

摘要 基于Transformer的方法在低级视觉任务&#xff08;如图像超分辨率&#xff09;上表现出令人印象深刻的性能。然而&#xff0c;我们发现这些网络只能通过归因分析利用有限的输入信息空间范围。这意味着Transformer的潜力在现有网络中仍未得到充分利用。为了激活更多输入像…

Spring 的创建和日志框架的整合

目录 一、第一个 Spring 项目 1、配置环境 2、Spring 的 jar 包 Maven 项目导入 jar 包和设置国内源的方法&#xff1a; 3、Spring 的配置文件 4、Spring 的核心 API ApplicationContext 4、程序开发 5、细节分析 &#xff08;1&#xff09;名词解释 &#xff08;2&…

uboot 顶层Makefile-make xxx_deconfig过程说明三

一. uboot 的 make xxx_deconfig配置 本文接上一篇文章的内容。地址如下&#xff1a;uboot 顶层Makefile-make xxx_deconfig过程说明二_凌肖战的博客-CSDN博客 本文继续来学习 uboot 源码在执行 make xxx_deconfig 这个配置过程中&#xff0c;顶层 Makefile有关的执行思路。 …

STM32纯中断方式发送接收数据(串行通信;keil arm5;)

除了main文件其他文件均无修改&#xff0c;正常运行--在keil arm5内

AJAX学习笔记9 搜索联想自动补全

AJAX学习笔记8 跨域问题及解决方案_biubiubiu0706的博客-CSDN博客 其实就一个功能 搜索联想 自动补全 键盘按下事件keydown 键盘弹起事件keyup 做模糊查询 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><t…

第9节-PhotoShop基础课程-移动抓手缩放工具

文章目录 前言1. 移动工具1.移动工具1.自动选择&#xff08;图层和组&#xff09;2.显示变换控件 &#xff08;Shift 变换/ Ctrl 变换&#xff09;3.自由变换 Ctrl T &#xff08;Shift 变换/ Ctrl 变换&#xff09;4.对齐功能 2.画板工具 V1. 创建画板并作图2.导出画板 2.路…

59、SpringBoot 自定义JSON的序列化器和反序列化器

Serialization(序列化)&#xff1a; 将java对象以一连串的字节码保存在磁盘文件中的过程&#xff0c;也可以说是保存java对象状态的过程。序列化可以将数据永久保存在磁盘上(通常保存在文件中)。 deserialization(反序列化)&#xff1a; 将保存在磁盘文件中的java字节码重新转…

Jmx协议远程连接java服务器

注意&#xff1a;本例里&#xff0c;我用的是jdk17 通常用jdk自带的jconsole&#xff0c;或者想要功能强大点的使用visualVM 需要java服务器在启动的时候加上以下参数 -Dcom.sun.management.jmxremote 启用jxm远程连接-Djava.rmi.server.hostname10.1.3.99 指定jxm监听地址&…

python 自(1)定义变量 数据类型 判断数据类型 转换数据类型 算数运算符 复合运算符 比较运算符 逻辑运算符 赋值运算符

注释 # 注释 就是一个 # 也可以 ctrl / 也可以出来注释 命名规范 # 标识符 由字母 下划线 数字 组成 数字不能开头 # 不可以使用 关键字 # 严格区分大小写 定义变量 # 变量定义 重复利用 # 例如 cons 你好小周 print(cons) print(cons)print("你好小周") #这…

【经典小练习】JavaSE—拷贝文件夹

&#x1f38a;专栏【Java小练习】 &#x1f354;喜欢的诗句&#xff1a;天行健&#xff0c;君子以自强不息。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f384;效果&#x1f33a;代码&#x1f6f8;讲解&#x…

每日一题 2596. 检查骑士巡视方案

难度&#xff1a;中等 很简单&#xff0c;从第 0 步开始模拟即可&#xff0c;唯一sb的就是测试用例中如果&#xff08;0&#xff0c;0&#xff09;处不为0的话就直接false&#xff0c;而不是去找0在哪 我的代码&#xff1a; class Solution:def checkValidGrid(self, grid: L…

【PowerQuery】Excel的PowerQuery的复制

在Excel中构建符合要求的PowerQuery连接之后&#xff0c;所有的PowerQuery 连接已经顺利的保存在Excel 工作簿当中&#xff0c;但是如何去查看已经保存的PowerQuery连接呢&#xff1f;图6.3 显示了查看PowerQuery连接。 Excel界面->数据页签->查询与连接 如果你的Power…

python求解一维弦振动方程

1、一维弦振动方程 2、差分公式及求解 import numpy as np import matplotlib.pyplot as plt l1 #长度 nx 101 #x方向网格节点数量 dx l/(nx-1) #空间网格步长 t6 #总时间 dt 0.01 # 时间步长 t_setnp.array([0.00,0.10,0.20,0.30,0.40,0.50,0.60]) #需要观察的时…