当前位置: 首页 > news >正文

C++---由优先级队列认识仿函数

文章目录

一、优先级队列是什么?

二、如何使用优先级队列

1、优先级队列容器用法

2、为什么容器本身无序?

三、什么是仿函数?

1. 什么是仿函数?

2. 仿函数的优势

四、仿函数如何使用?

1、重载operator()函数

2、运用第三个参数模板

3、大小堆切换 

大堆测试代码:

小堆测试代码:

4、头文件总代码 

五、什么是容器适配器?


前言

  本文主要介绍了优先级队列是什么,如何使用优先级队列,并且由优先级队列引出仿函数,从中认识仿函数,最后了解一下什么是适配器。


一、优先级队列是什么?

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、优先级队列容器用法

我们从cplusplus网站中看一些优先级队列的结构:

  优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成 堆的结构,因此priority_queue就是堆所有需要用到堆的位置,都可以考虑使用priority_queue。注意: 默认情况下priority_queue是大堆。

  我们用一段代码来带大家初步认识:

#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{// 默认情况下,创建的是大堆,其底层按照小于号比较vector<int> v{3,2,7,6,0,4,1,9,8,5};priority_queue<int> q1;for (auto& e : v)q1.push(e);cout << q1.top() << endl;// 如果要创建小堆,将第三个模板参数换成greater比较方式priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;
}

  这段代码打印的结果是堆顶的数据,如果是大堆,那么堆顶就是最大的,反之堆顶的数据就是最小的。

打印结果:

  打印第一行就是默认大堆的结果,第二行是我们增加了参数模板改成了小堆。

  我们看到这里第一想法就是,可以用优先级队列来排序,是的没错,但是你将容器中的数打印出来却发现并不是有序的,只是符合了大堆的性质

2、为什么容器本身无序?

  我们都知道了他是大堆,每次取出顶部元素之后删除顶部元素再进行向下调整取出第二个最大元素,所以我们就知道,有序的不是容器本身,而是我们从堆顶依次取出的数据。

  我们用默认大堆,将堆顶的数据依次取出查看顺序结果:

while (!q1.empty())
{cout << q1.top() << " ";q1.pop();
}
cout << endl;

三、什么是仿函数?

 在我们上面优先级队列使用时,我们想将默认大堆改成小堆,因此我们添加了额外的两个参数模板,其中控制大小堆变化的就是第三个参数greater<int>

  在C++中,仿函数或函数对象是通过重载operator()的类实例来模拟函数行为的对象。这种特性使得C++的对象可以像函数一样被调用,从而为编程提供了极大的灵活性和强大的功能。

1. 什么是仿函数?

仿函数是一个类,它定义了一个或多个operator()成员函数,使得其对象可以像普通函数那样被调用。仿函数通常用于以下场景:

  • 作为算法的比较函数
  • 作为算法的操作函数
  • 存储状态或属性,使行为可定制

2. 仿函数的优势

与普通函数和函数指针相比,仿函数具有以下优势:

  • 状态维护:仿函数可以持有状态,每次调用可以根据状态改变行为。
  • 内联调用:由于仿函数是通过对象调用的,编译器可以轻易地将其内联,减少调用开销。
  • 高度定制:可以通过对象的属性来调整其行为。

四、仿函数如何使用?

我们通过对优先级队列的实现,写出一个可以作为比较函数的仿函数

我们先在头文件中写出默认大堆的代码,实现优先级队列的几个功能:

代码如下:

#pragma once
#include<queue>
#include<vector>
#include<algorithm>using namespace std;
namespace bit
{template<class T, class Container = vector<T>>class priority_queue{public:void adjust_up(size_t child){size_t parent = (child - 1) / 2;while (child > 0){if (_con[parent]< _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){size_t child = parent * 2 + 1;while (child < _con.size()){if ( child + 1 < _con.size()&& _con[child]< _con[child + 1]){child++;}if (_con[parent]<_con[child]){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}bool empty(){return _con.empty();}const T& top(){return _con[0];}size_t size(){return _con.size();}private:Container _con;};
}

我们这个是默认大堆的,创建对象之后,每次取出堆顶的数据只会是最大了那个数据,因为我们在向上调整或者向下调整时,全都是大堆的比较方法,所以我们只能用大堆。

那我们应该如何切换小堆呢?

1、重载operator()函数

我们重载operator()函数使其成为一个可以被调用的可以比较大小的函数

代码如下:

	template<class T>class less{public:bool operator()(const T& x, const T& y){return x < y;}};template<class T>class greater{public:bool operator()(const T& x, const T& y){return x > y;}};

在这两个比较函数中,less就是大堆的比较方法,而greater就是小堆的比较方法 

2、运用第三个参数模板

我们知道,我们所写的operator()函数是在里面,所以这个类就可以作为一个类模板去使用 

我们在第三个参数模板中写一个比较模板,用来切换在向上调整或者向下调整中的比较方法,进而去切换大小堆

模板代码:

template<class T, class Container = vector<T>,class Compare=less<T>>

我们将第三个模板命名为Compare ,后面调用less类的比较方法,在默认情况下仍是大堆。

接下来,我们可以把向上调整或者向下调整中的比较方法修改成我们的仿函数。

先创建Compare对象

Compare com;

接下来开始替换(向上调整举例): 

void adjust_up(size_t child)
{Compare com;size_t parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

我们可以看到,我们在if判断语句中的比较已经改成了我们的仿函数。

3、大小堆切换 

当我们想要从大堆切换小堆时,我们直接改变第三个参数模板的底层类就可以了,将less<T>修改成greater<T>即可(头文件和源文件的模板都要修改)

template<class T, class Container = vector<T>,class Compare=greater<T>>

我们进行一下测试:

大堆测试代码:

void Test_priority_queue()
{bit::priority_queue<int,vector<int>,less<int>> pq;pq.push(2);pq.push(7);pq.push(1);pq.push(8);while (!pq.empty()){	cout << pq.top() << " ";pq.pop();}cout << endl;
}
int main()
{Test_priority_queue();return 0;
}	

打印结果:

小堆测试代码:

void Test_priority_queue()
{bit::priority_queue<int,vector<int>,greater<int>> pq;pq.push(2);pq.push(7);pq.push(1);pq.push(8);while (!pq.empty()){	cout << pq.top() << " ";pq.pop();}cout << endl;
}
int main()
{Test_priority_queue();return 0;
}	

打印结果: 

 

4、头文件总代码 

#pragma once
#include<queue>
#include<vector>
#include<algorithm>using namespace std;
namespace bit
{//仿函数,切换大堆小堆,仿函数作为一个类型,可以作为类模板使用template<class T>class less{public:bool operator()(const T& x, const T& y){return x < y;}};template<class T>class greater{public:bool operator()(const T& x, const T& y){return x > y;}};template<class T, class Container = vector<T>,class Compare=greater<T>>class priority_queue{public:void adjust_up(size_t child){Compare com;size_t parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){Compare com;size_t child = parent * 2 + 1;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]);parent = child;child = parent * 2 + 1;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}bool empty(){return _con.empty();}const T& top(){return _con[0];}size_t size(){return _con.size();}private:Container _con;};
}

五、什么是容器适配器?

  适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口

相关文章:

C++---由优先级队列认识仿函数

文章目录 一、优先级队列是什么&#xff1f; 二、如何使用优先级队列 1、优先级队列容器用法 2、为什么容器本身无序&#xff1f; 三、什么是仿函数&#xff1f; 1. 什么是仿函数&#xff1f; 2. 仿函数的优势 四、仿函数如何使用&#xff1f; 1、重载operator()函数 2、运用第…...

Client访问Server访问慢的原因

1. 网络层面的问题 网络延迟&#xff1a;客户端与服务器之间的地理距离较远(跨ISP、路径次优&#xff09;&#xff0c;导致高网络延迟&#xff08;如高 RTT 值&#xff09;。使用 ping 或 traceroute 工具可以帮助定位网络延迟的来源 - mtr: 结合了ping和traceroute功能&#…...

用RPC Performance Inspector 优化你的区块链

目录 什么是RPC&#xff1f; RPC Performance Inspector 是做什么的&#xff1f; 为什么需要这个工具&#xff1f; 如何使用它&#xff1f; 适合谁用&#xff1f; 如何使用&#xff1f; 什么是RPC&#xff1f; RPC Performance Inspector 是一个专门用于测试和分析RPC性能…...

linux如何查看内存条是ddr几代

在 Linux 系统中&#xff0c;可以通过以下几种方法查看内存条的类型和代数&#xff08;如 DDR3、DDR4 等&#xff09;&#xff1a; 1. 使用 dmidecode 命令 dmidecode 是一个工具&#xff0c;它可以从系统的 DMI 表&#xff08;也称为 SMBIOS 表&#xff09;中提取硬件信息&a…...

LeetCode 3153.所有数对中数位差之和:计数

【LetMeFly】3153.所有数对中数位差之和&#xff1a;计数 力扣题目链接&#xff1a;https://leetcode.cn/problems/sum-of-digit-differences-of-all-pairs/ 车尔尼有一个数组 nums &#xff0c;它只包含 正 整数&#xff0c;所有正整数的数位长度都 相同 。 两个整数的 数位…...

Spring Boot 整合 Sentinel 实现流量控制

在微服务架构中&#xff0c;流量控制是保障系统稳定性和高可用性的关键技术之一。阿里巴巴开源的 Sentinel 是一款面向分布式系统的流量防护组件&#xff0c;旨在从流量控制、熔断降级、系统负载保护等多个维度保障服务的稳定性。本文将详细介绍如何在 Spring Boot 项目中整合 …...

Elasticsearch倒排索引

什么是倒排索引 倒排索引&#xff08;Inverted Index&#xff09;是一种将文档中的每个单词映射到包含该单词的文档列表上的数据结构 倒排索引的构建过程 文档1: “我爱吃苹果” 文档2: “我爱吃香蕉” 文档3: “我喜欢苹果和香蕉” 文档分词&#xff1a;将文档中的文本内容…...

速盾:ddos常用防御方法是什么?

DDoS攻击是一种通过向网络资源发送大量请求或大流量数据来使其过载的攻击手段。为了应对这种攻击&#xff0c;常用的防御方法可以分为三个层次&#xff1a;流量清洗、服务器升级和高防CDN。 流量清洗是一种基础的防御手段&#xff0c;它通过过滤和识别恶意流量来阻止DDoS攻击。…...

二分算法入门(简单题)

习题1 704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], targ…...

在使用React Hooks中,如何避免状态更新时的性能问题?

在React Hooks中避免状态更新时的性能问题&#xff0c;可以采取以下一些最佳实践&#xff1a; 避免不必要的状态更新&#xff1a; 使用React.memo、useMemo、和useCallback来避免组件或其子组件进行不必要的渲染。 使用useMemo&#xff1a; 对于基于状态或props的复杂计算&…...

Pytest插件pytest-selenium-让自动化测试更简洁

在现代Web应用的开发中&#xff0c;自动化测试成为确保网站质量的重要手段之一。而Pytest插件 pytest-selenium 则为开发者提供了简单而强大的工具&#xff0c;以便于使用Python进行Web应用的自动化测试。本文将深入介绍 pytest-selenium 插件的基本用法和实际案例&#xff0c;…...

视觉语言模型(VLMs)知多少?

最近这几年&#xff0c;自然语言处理和计算机视觉这两大领域真是突飞猛进&#xff0c;让机器不仅能看懂文字&#xff0c;还能理解图片。这两个领域的结合&#xff0c;催生了视觉语言模型&#xff0c;也就是Vision language models (VLMs) &#xff0c;它们能同时处理视觉信息和…...

重新修改 Qt 项目的 Kit 配置

要重新修改 Qt 项目的 Kit 配置&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 打开 Qt Creator 首先&#xff0c;启动 Qt Creator&#xff0c;确保你的项目已经打开。 2. 进入项目设置 在 Qt Creator 中&#xff0c;点击菜单栏的 “Projects” 标签&#xff08;通…...

【Spring Boot 3】【Web】自定义响应状态码

【Spring Boot 3】【Web】自定义响应状态码 背景介绍开发环境开发步骤及源码工程目录结构背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费…...

Locksupport凭证的底层原理

LockSupport的凭证&#xff08;通常称为“许可”或“permit”&#xff09;的底层原理主要涉及到Java的Unsafe类以及系统级的线程同步机制。LockSupport是Java 6&#xff08;JSR166-JUC&#xff09;引入的一个类&#xff0c;提供了基本的线程同步原语&#xff0c;其核心功能是通…...

Elasticsearch 再次开源

作者&#xff1a;来自 Elastic Shay Banon [D.N.A] Elasticsearch 和 Kibana 可以再次被称为开源了。很难表达这句话让我有多高兴。我真的激动得跳了起来。Elastic 的所有人都是这样的。开源已经融入我的 DNA&#xff0c;也融入了 Elastic 的 DNA。能够再次将 Elasticsearch 称…...

对称密码学

1. 使用OpenSSL 命令行 在 Ubuntu Linux Distribution (发行版&#xff09;中&#xff0c; OpenSSL 通常可用。当然&#xff0c;如果不可用的话&#xff0c;也可以使用下以下命令安装 OpenSSL: $ sudo apt-get install openssl 安装完后可以使用以下命令检查 OpenSSL 版本&am…...

正则表达式优化建议

文章目录 优化正则表达式代码示例&#xff1a;注意事项&#xff1a; 一些常见的正则表达式陷阱 优化正则表达式是提高文本处理效率和准确性的重要步骤。以下是一些优化正则表达式的方法&#xff1a; 以下是整理归纳后的正则表达式优化技巧&#xff1a; 优化正则表达式 一、预…...

Oracle RAC关于多节点访问同一个数据的过程

一、说明 Oracle RAC 存在多个计算节点&#xff0c;但是使用的共享存储。那么多个节点共同访问同一个资源&#xff0c;怎么保证一致性。 白文的逻辑理解简述&#xff1a; 用户1访问rac1 &#xff0c;通过rac1获取AA数据块后&#xff0c;会加上latch锁。用户2通过rac2访问AA数据…...

IPC$漏洞多位密码爆破方法

虽然不应该将其用于非法的密码破解行为,但从代码修改角度来说,如果要破解多位密码(比如 n 位),你可以按照以下方式调整: 破解多位纯数字密码 如果你想破解 6 位纯数字密码: FOR /L %%i IN (100000,1,999999) DO (net use \\target - ip\ipc$ /user:weak %%i &&…...

Spring Data 2026 最佳实践:简化数据访问

Spring Data 2026 最佳实践&#xff1a;简化数据访问别叫我大神&#xff0c;叫我 Alex 就好。一、引言 大家好&#xff0c;我是 Alex。Spring Data 作为 Spring 生态系统中的重要组成部分&#xff0c;一直以其简化数据访问的能力而受到开发者的喜爱。随着 Spring Data 2026 的发…...

SAR信号处理中的汉宁窗优化——旁瓣抑制与分辨率平衡的艺术

1. 汉宁窗在SAR信号处理中的核心作用 我第一次接触汉宁窗是在处理火星探测器雷达数据时遇到的棘手问题。当时团队获取的火星次表层雷达图像出现了严重的旁瓣干扰&#xff0c;就像在干净的画布上泼洒了墨水点。导师随手调出汉宁窗函数说&#xff1a;"试试这个魔法棒"—…...

回溯算法双杀:子集 + 电话号码的字母组合 | 经典模板题解析

目录 一、LeetCode 78&#xff1a;子集 题目描述 核心思路&#xff08;回溯法&#xff09; 完整代码 关键解析 二、LeetCode 17&#xff1a;电话号码的字母组合 题目描述 核心思路&#xff08;回溯法&#xff09; 完整代码 关键解析 三、两道题核心对比 总结 一、L…...

零基础玩转DeepSeek-R1推理模型:Ollama一键部署Llama-8B教程

零基础玩转DeepSeek-R1推理模型&#xff1a;Ollama一键部署Llama-8B教程 1. 引言&#xff1a;为什么选择DeepSeek-R1-Distill-Llama-8B 你是否想体验强大的文本生成能力&#xff0c;却被复杂的模型部署流程劝退&#xff1f;DeepSeek-R1-Distill-Llama-8B是一个经过优化的8B参…...

LangGraph条件边实战:手把手教你打造一个能‘看图说话’的客服工单分流Agent

LangGraph条件边实战&#xff1a;打造智能客服工单分流系统 想象一下&#xff0c;当用户向客服系统发送"我要退款"或"查询物流"这样的请求时&#xff0c;系统能像经验丰富的客服主管一样&#xff0c;瞬间理解意图并将工单精准路由到对应处理部门。这不再是…...

什么是共轭表达式?解决了什么问题?

什么是共轭表达式&#xff1f;解决了什么问题&#xff1f;为什么导数是 1/x&#xff1f; 导数衡量的是“每增加 1 单位的 xxx&#xff0c;y 能增加多少”...

Yii2的EVENT_BEFORE_ACTION的本质的庖丁解牛

yii\base\Controller::EVENT_BEFORE_ACTION 是 Yii2 框架中 AOP&#xff08;面向切面编程&#xff09; 的核心锚点&#xff0c;也是 MVC 流程中的“安检门”。 它的本质是&#xff1a;在具体的业务逻辑&#xff08;Action&#xff09;执行之前&#xff0c;提供的一个“拦截、验…...

高性能数据库集群

近年来各种存储技术飞速发展&#xff0c;但关系数据库由于其 ACID 的特性和功能强大的 SQL 查询&#xff0c;目前还是各种业务系统中关键和核心的存储系统&#xff0c;很多场景下高性能的设计最核心的部分就是关系数据库的设计。 不管是为了满足业务发展的需要&#xff0c;还是…...

PINN实战:如何用PyTorch自定义神经网络结构求解偏微分方程?

PINN实战&#xff1a;PyTorch自定义神经网络架构设计指南 在科学计算领域&#xff0c;物理信息神经网络(PINN)正逐渐成为求解偏微分方程(PDE)的新范式。与传统的数值方法不同&#xff0c;PINN将物理方程直接编码到神经网络中&#xff0c;通过自动微分技术实现端到端的求解。本文…...

智能编程伙伴:让快马ai辅助你优化与调试keil嵌入式项目代码

智能编程伙伴&#xff1a;让快马AI辅助你优化与调试Keil嵌入式项目代码 最近在Keil MDK环境下开发STM32G474RET6的精密数据采集系统时&#xff0c;遇到了ADC采样噪声大和实时性不足的问题。作为一个嵌入式开发者&#xff0c;这些问题直接影响系统的精度和响应速度。通过使用In…...