【C++】priority_queue仿函数
今天我们来学习C++中另一个容器适配器:优先级队列——priority_queue;和C++一个重要组件仿函数:
目录
一、priority_queue
1.1 priority_queue是什么
1.2 priority_queue的接口
1.2.1 priority_queue使用举例
二、仿函数
三、关于priority_queue的例题
四、模拟实现priority_queue
五、priority_queue的使用拓展
一、priority_queue
1.1 priority_queue是什么
priority_queue介绍文档:priority_queue - C++ Reference (cplusplus.com)
优先队列(priority_queue)是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
其底层就是堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。对于堆不熟悉的可以看到这里:【精选】【数据结构初阶】树与二叉树——堆
优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类(默认使用vector)。容器应该可以通过随机访问迭代器访问,并支持以下操作: empty():检测容器是否为空、size():返回容器中有效元素个数 、front():返回容器中第一个元素的引用、push_back():在容器尾部插入元素、pop_back():删除容器尾部元素
标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作
1.2 priority_queue的接口
| 函数声明 | 接口说明 |
|---|---|
| priority queue()/priority queue(first,last) | 构造一个空的优先级队列() |
| empty | 检测优先级队列是否为空,是返回true,否则返回 false |
| size | 返回优先级队列中元素总个数 |
| top | 返回优先级队列中最大(最小元素),即堆顶元素 |
| push | 在优先级队列中插入元素 |
| pop | 删除优先级队列中最大(最小)元素,即堆顶元素 |
1.2.1 priority_queue使用举例
#include<iostream>
#include<vector>
#include<queue>
#include<functional>using namespace std;
int main()
{priority_queue<int> q1;//大堆q1.push(5);q1.push(1);q1.push(10);q1.push(8);q1.push(7);while (!q1.empty()){cout<<q1.top()<<" ";q1.pop();}cout << endl;priority_queue<int,vector<int>,greater<int>> q2;//小堆q2.push(5);q2.push(1);q2.push(10);q2.push(8);q2.push(7);while (!q2.empty()){cout << q2.top() << " ";q2.pop();}return 0;
}
运行效果:

我们可以看到构建一个小堆要用到仿函数,下面我们来看看仿函数是个什么东西:
二、仿函数
我们先来看到下面这段代码:
struct ADD
{int operator()(int x, int y){return x + y;}
};
int main()
{struct ADD add;cout << add(1,9) << endl;return 0;
}
运行效果:

我们可以看到,该段代码在结构体中实现了一个()的运算符重载,接着使用该结构体定义了一个对象add,用该对象调用里面的运算符重载函数实现了一个功能,这就是大名鼎鼎的仿函数
由此看来仿函数的本质就是()的运算符重载,使创建的对象可以像函数一样使用
三、关于priority_queue的例题
题目地址:
215. 数组中的第K个最大元素
解题思路:对于该使一个经典的top-k问题,对于题我们可以先取k个数建立一个小堆,再将后面的数依次与堆顶元素相比较,如果比堆顶元素大就将堆顶元素丢弃后,将该数入堆;接着再接着与下一个数相比,这样最终堆顶元素就是第K个最大的数了:
解题代码:
class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int,vector<int>,greater<int>> q(nums.begin(),nums.begin()+k);for(int i=k;i<nums.size();++i){if(nums[i]>q.top()){q.pop();q.push(nums[i]);}}return q.top();}
};

四、模拟实现priority_queue
下面我们来手搓一个priority_queue:
#pragma once
#include<iostream>
#include<vector>
#include<algorithm>namespace lhs
{template<class T, class container = std::vector<T>>class priority_queue{private:void adjustup(size_t child)//向上调整{while (child > 0){size_t parent = (child - 1) / 2;if (_con[child] > _con[parent]){std::swap(_con[child], _con[parent]);child = parent;}else break;}}void adjustdown(size_t parent)//向下调整{size_t child = parent * 2 + 1;while (child < size()){if (child + 1 < size() && _con[child] < _con[child + 1]){++child;}if (_con[parent] < _con[child]){std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else break;}}public:void push(const T& x){_con.push_back(x);adjustup(size() - 1);}void pop(){std::swap(_con[0], _con[size() - 1]);_con.pop_back();adjustdown(0);}size_t size(){return _con.size();}bool empty(){return _con.empty();}const T& top(){return _con[0];}private:container _con;};
}
测试代码:
int main()
{lhs::priority_queue<int> q;q.push(1);q.push(2);q.push(3);q.push(4);q.push(10);q.push(18);while (!q.empty()){std::cout << q.top() << " ";q.pop();}std::cout << std::endl;return 0;
}
运行效果:

但是上面手搓的priority_queue只能实现大堆的效果,要实现小堆怎么办?我们不可能手动修改代码中向上和向下调整函数中的判断符吧?
这里就要轮到仿函数登场了:
#pragma once
#include<iostream>
#include<vector>
#include<algorithm>namespace lhs
{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 = std::vector<T>, class compare = less<T>>class priority_queue{private:void adjustup(size_t child)//向上调整{compare com;while (child > 0){size_t parent = (child - 1) / 2;if (com(_con[child], _con[parent])){std::swap(_con[child], _con[parent]);child = parent;}else break;}}void adjustdown(size_t parent)//向下调整{compare com;size_t child = parent * 2 + 1;while (child < size()){if (child + 1 < size() && com(_con[child + 1], _con[child])){++child;}if (com(_con[child], _con[parent])){std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else break;}}public:void push(const T& x){_con.push_back(x);adjustup(size() - 1);}void pop(){std::swap(_con[0], _con[size() - 1]);_con.pop_back();adjustdown(0);}size_t size(){return _con.size();}bool empty(){return _con.empty();}const T& top(){return _con[0];}private:container _con;};
}
我们可以看到,我们上面定义了两个仿函数:less(构建大堆)和greater(构建小堆),我们在测试代码中传入我们所要选择的仿函数类型来控制大小堆的构建(本质是通过不一样的仿函来控制运算符的改变):
int main()
{//lhs::priority_queue<int> q;//大堆lhs::priority_queue<int, std::deque<int>, lhs::greater<int>> q;//小堆q.push(1);q.push(2);q.push(3);q.push(4);q.push(10);q.push(18);while (!q.empty()){std::cout << q.top() << " ";q.pop();}std::cout << std::endl;return 0;
}
运行效果:

五、priority_queue的使用拓展
我们把之前写的日期类拿出来:
class Date
{friend ostream& operator<<(ostream& out, const Date& d);
public:Date(int year, int month, int day);//构造函数int GetMonthDay(int year, int month) const;//获取year年month月的天数//运算符重载bool operator==(const Date& d) const;//判断日期是否相同bool operator!=(const Date& d) const;//判断日期是否不相同bool operator<(const Date& d) const;//判断当前日期是否在传入日期之前bool operator<=(const Date& d) const;//判断当前日期是否在传入日期之前或相同bool operator>(const Date& d) const;//判断当前日期是否在传入日期之后bool operator>=(const Date& d) const;//判断当前日期是否在传入日期之后或相同private:int _year;int _month;int _day;
};int Date::GetMonthDay(int year, int month) const
{int MonthDay[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };//用数组来依次存储平年1到12月的天数if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))//判断是否为闰年的2月{return 29;}return MonthDay[month - 1];
}
Date::Date(int year, int month, int day)
{if (month < 1 || month>12 || (day<1 || day>GetMonthDay(year, month)))//判断日期是否合法{cout << "Illegal date!" << endl;}else{_year = year;_month = month;_day = day;}
}
bool Date::operator==(const Date& d) const
{return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator!=(const Date& d) const
{return !(*this == d);
}
bool Date::operator<(const Date& d) const
{return _year < d._year || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day);
}
bool Date::operator<=(const Date& d) const
{return (*this < d) || (*this == d);
}
bool Date::operator>(const Date& d) const
{return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{return !(*this < d);
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "/" << d._month << "/" << d._day << endl;return out;
}
下面我们可以使用priority_queue对自己写的日期类进行存储:
int main()
{Date d1(2022, 10, 23);Date d2(2022, 10, 25);Date d3(2022, 10, 24);priority_queue<Date> q1;q1.push(d1);q1.push(d2);q1.push(d3);cout << q1.top() << endl;priority_queue<Date,vector<Date>, greater<Date>> q2;q2.push(d1);q2.push(d2);q2.push(d3);cout << q2.top() << endl;return 0;
}
运行效果:

因为我们自实现的Date重载了<和>运算符,所以在我们使用priority_queue来存储时,其内部会自动调用我们所写的运算符重载来构建大小堆
再看到下面的代码:
int main()
{priority_queue<Date*> q1;q1.push(new Date(2022, 10, 23));q1.push(new Date (2022, 10, 25));q1.push(new Date (2022, 10, 24));cout << *q1.top() << endl;return 0;
}
多次运行效果:

咦?怎么每次运行的结果都不一样?
仔细看,这里我们传入的存储类型是Date*,而对于指针类型,按默认的<和>运算符来比较这是比较指针所指向地址空间的大小,但new创建空间时地址是随机的,所以这是不合理的
下面我们来写两个仿函数实现一下Date*类型的比较:
class Date_less
{
public:bool operator()(Date*a, Date* b){return *a < * b;}
};class Date_greater
{
public:bool operator()(Date* a, Date* b){return *a > *b;}
};
int main()
{priority_queue<Date*,vector<Date*>,Date_less> q1;priority_queue<Date*, vector<Date*>, Date_greater> q2;q1.push(new Date(2022, 10, 23));q1.push(new Date (2022, 10, 25));q1.push(new Date (2022, 10, 24));cout << *q1.top() << endl;q2.push(new Date(2022, 10, 23));q2.push(new Date(2022, 10, 25));q2.push(new Date(2022, 10, 24));cout << *q2.top() << endl;return 0;
}
在使用priority_queue时传入我们所写的仿函数就可以实现Date*类型的大小堆存储啦
综上所述,泛型和运算符重载为我们提供了极大的方便,我们如果对系统所提供的东西不满意,就可以使用自己所定义的方法,一切都自在掌控~
相关文章:
【C++】priority_queue仿函数
今天我们来学习C中另一个容器适配器:优先级队列——priority_queue;和C一个重要组件仿函数: 目录 一、priority_queue 1.1 priority_queue是什么 1.2 priority_queue的接口 1.2.1 priority_queue使用举例 二、仿函数 三、关于priority…...
如何驾驭ChatGPT:掌控有效对话!
📢📢📢📣📣📣 哈喽!大家好,我是【一心同学】,一位上进心十足的【后端领域博主】!😜😜😜 ✨【一心同学】的写作风格&#x…...
LeetCode 面试题 16.03. 交点
文章目录 一、题目二、C# 题解 一、题目 给定两条线段(表示为起点 start {X1, Y1} 和终点 end {X2, Y2}),如果它们有交点,请计算其交点,没有交点则返回空值。 要求浮点型误差不超过 10^-6。若有多个交点(…...
【码银送书第九期】《ChatGPT 驱动软件开发:AI 在软件研发全流程中的革新与实践》
计算机技术的发展和互联网的普及,使信息处理和传输变得更加高效,极大地改变了金融、商业、教育、娱乐等领域的运作方式。数据分析、人工智能和云计算等新兴技术,也在不断地影响和改变着各个行业。 如今,我们正在见证人工智能技术的…...
Hadoop3.0大数据处理学习4(案例:数据清洗、数据指标统计、任务脚本封装、Sqoop导出Mysql)
案例需求分析 直播公司每日都会产生海量的直播数据,为了更好地服务主播与用户,提高直播质量与用户粘性,往往会对大量的数据进行分析与统计,从中挖掘商业价值,我们将通过一个实战案例,来使用Hadoop技术来实…...
华为机试题:HJ3 明明的随机数
目录 第一章、算法题1.1)题目描述1.2)解题思路与答案1.3)牛客链接 友情提醒: 先看文章目录,大致了解文章知识点结构,点击文章目录可直接跳转到文章指定位置。 第一章、算法题 1.1)题目描述 题目描述&…...
Python OpenCV将n×n的小图拼接成m×m的大图
Python OpenCV将nn的小图拼接成mm的大图 前言前提条件相关介绍实验环境n \times n的小图拼接成m \times m的大图代码实现 前言 由于本人水平有限,难免出现错漏,敬请批评改正。更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小…...
wkhtmltoimage/wkhtmltopdf 使用实践
1. 介绍 wkhtmltopdf/wkhtmltoimage 用于将简单的html页面转换为pdf或图片; 2.安装 downloads 2.1. mac os 下载64-bit 版本然后按照指示安装, 遇到 untrust developers 时,需要在 Settings -> Privacy 处信任下该安装包。 2.2. debian # 可用…...
Rclone连接Onedrive
一、Rclone介绍 Rclone是一款的命令行工具,支持在不同对象存储、网盘间同步、上传、下载数据。 我们这里连接的onedrive,其他网盘请查看官方文档。 注意: 需要先在Windows下配置好了,然后再将rclone配置文件复制到Linux的rclone配…...
RK356X/RK3588构建Ubuntu20.04根文件系统
文章目录 前言一、官网下载ubuntu-base二、挂载并构建文件系统2.1、配置构建文件系统环境2.2、编写挂载脚本mount.sh并安装相关工具2.3、轻量级的桌面环境 lubuntu-desktop2.4、卸载一些不必要的软件2.5、添加用户2.6 、允许root用户登录桌面2.7、串口自动登录2.8、添加分区释放…...
本地新建项目如何推到码云上去
1.先在码云上建立一个空仓库,正常步骤就行。建立完成有readme.md. 2.然后本地建立项目文件,正常脚手架搭建VUE\REACT等。记得要项目git init一下。 3.本地改好的内容commit 一下。 4.本地文件与远端仓库建立连接。git remote add origin https://gite…...
RSAUtil 前端 JavaScript JSEncrypt 实现 RSA (长文本)加密解密
文章归档:https://www.yuque.com/u27599042/coding_star/cl4dl599pdmtllw1 依赖 import JSEncrypt from ‘jsencrypt’ pnpm i jsencryptimport {stringIsNull} from “/utils/string_utils.js”:https://www.yuque.com/u27599042/coding_star/slncupw…...
uniapp map polygons 区域填充色(fillColor)在ios显示正常,但在安卓手机显示是黑色的,怎么解决?
uniapp map polygons 区域填充色(fillColor)在ios显示正常,但在安卓手机显示是黑色的,怎么解决? <MapPage :longitude"item.centerCoord[0]" :latitude"item.centerCoord[1]":polygons"[{ points: it…...
OSCAR数据库上锁问题如何排查
关键字 oscar lock 问题描述 oscar 数据库上锁问题如何排查 解决问题思路 准备数据 create table lock_test(name varchar(10),age varchar(10));insert into lock_test values(ff,10); insert into lock_test values(yy,20); insert into lock_test values(ll,30);sessio…...
FPGA与人工智能泛谈-01
文章目录 前言一、FPGA(Field Programmable Gate Array)是什么?二、与GPU的对比1.GPU特点2. FPGA的优势三、人工智能实现的基础架构总结前言 人工智能技术的快速发展正从各个方面改变人类的生活、工作及教育等各个方面,其中人工智能算法的演进又是其中的关键一步,其中会涉及…...
【VASP】POTCAR文件
【VASP】POTCAR文件 POTCAR 文件的介绍qvasp 生成POTCARvaspkit 生成POTCAR再来认识一下各种赝势如何区分US、PAW、LDA、GGA、PW91 前言 一、4个常用的输入文件INCAR、POSCAR、POTCAR、KPOINTS INCAR: 计算任务类型是什么?怎么计算? KPOINTS: 包含了倒易…...
棒球俱乐部青少年成长体系·棒球1号位
棒球俱乐部青少年成长体系介绍 1. 培养理念 简要介绍棒球俱乐部的宗旨和培养青少年的目标 棒球俱乐部是一个致力于培养青少年棒球运动员的体育组织,其宗旨是通过提供专业的棒球训练和比赛机会,帮助青少年提高身体素质、培养团队合作精神和塑造积极向上…...
折叠式菜单怎么做编程,初学编程系统化教程初级1上线
中文编程系统化教程,不需英语基础,学习链接——入门篇课程 https://edu.csdn.net/course/detail/39036中文编程系统化教程,不需英语基础,学习链接—— 初级1课程 https://edu.csdn.net/course/detail/39061 ——————————…...
与AI对话,如何写好prompt?
玩转AIGC,优质的Prompt提示词实在是太重要了!同样的问题,换一个问法,就会得到差别迥异的答案。你是怎样和AI进行对话交流的呢?我来分享几个: 请告诉我…我想知道…对于…你有什么看法?帮我解决…...
基于YOLOv8模型和UA-DETRAC数据集的车辆目标检测系统(PyTorch+Pyside6+YOLOv8模型)
摘要:基于YOLOv8模型和UA-DETRAC数据集的车辆目标检测系统可用于日常生活中检测与定位汽车(car)、公共汽车(bus)、面包车(vans)等目标,利用深度学习算法可实现图片、视频、摄像头等方…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
