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++---由优先级队列认识仿函数
文章目录 一、优先级队列是什么? 二、如何使用优先级队列 1、优先级队列容器用法 2、为什么容器本身无序? 三、什么是仿函数? 1. 什么是仿函数? 2. 仿函数的优势 四、仿函数如何使用? 1、重载operator()函数 2、运用第…...
Client访问Server访问慢的原因
1. 网络层面的问题 网络延迟:客户端与服务器之间的地理距离较远(跨ISP、路径次优),导致高网络延迟(如高 RTT 值)。使用 ping 或 traceroute 工具可以帮助定位网络延迟的来源 - mtr: 结合了ping和traceroute功能&#…...

用RPC Performance Inspector 优化你的区块链
目录 什么是RPC? RPC Performance Inspector 是做什么的? 为什么需要这个工具? 如何使用它? 适合谁用? 如何使用? 什么是RPC? RPC Performance Inspector 是一个专门用于测试和分析RPC性能…...

linux如何查看内存条是ddr几代
在 Linux 系统中,可以通过以下几种方法查看内存条的类型和代数(如 DDR3、DDR4 等): 1. 使用 dmidecode 命令 dmidecode 是一个工具,它可以从系统的 DMI 表(也称为 SMBIOS 表)中提取硬件信息&a…...
LeetCode 3153.所有数对中数位差之和:计数
【LetMeFly】3153.所有数对中数位差之和:计数 力扣题目链接:https://leetcode.cn/problems/sum-of-digit-differences-of-all-pairs/ 车尔尼有一个数组 nums ,它只包含 正 整数,所有正整数的数位长度都 相同 。 两个整数的 数位…...
Spring Boot 整合 Sentinel 实现流量控制
在微服务架构中,流量控制是保障系统稳定性和高可用性的关键技术之一。阿里巴巴开源的 Sentinel 是一款面向分布式系统的流量防护组件,旨在从流量控制、熔断降级、系统负载保护等多个维度保障服务的稳定性。本文将详细介绍如何在 Spring Boot 项目中整合 …...
Elasticsearch倒排索引
什么是倒排索引 倒排索引(Inverted Index)是一种将文档中的每个单词映射到包含该单词的文档列表上的数据结构 倒排索引的构建过程 文档1: “我爱吃苹果” 文档2: “我爱吃香蕉” 文档3: “我喜欢苹果和香蕉” 文档分词:将文档中的文本内容…...
速盾:ddos常用防御方法是什么?
DDoS攻击是一种通过向网络资源发送大量请求或大流量数据来使其过载的攻击手段。为了应对这种攻击,常用的防御方法可以分为三个层次:流量清洗、服务器升级和高防CDN。 流量清洗是一种基础的防御手段,它通过过滤和识别恶意流量来阻止DDoS攻击。…...
二分算法入门(简单题)
习题1 704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], targ…...
在使用React Hooks中,如何避免状态更新时的性能问题?
在React Hooks中避免状态更新时的性能问题,可以采取以下一些最佳实践: 避免不必要的状态更新: 使用React.memo、useMemo、和useCallback来避免组件或其子组件进行不必要的渲染。 使用useMemo: 对于基于状态或props的复杂计算&…...

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

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

重新修改 Qt 项目的 Kit 配置
要重新修改 Qt 项目的 Kit 配置,你可以按照以下步骤进行操作: 1. 打开 Qt Creator 首先,启动 Qt Creator,确保你的项目已经打开。 2. 进入项目设置 在 Qt Creator 中,点击菜单栏的 “Projects” 标签(通…...
【Spring Boot 3】【Web】自定义响应状态码
【Spring Boot 3】【Web】自定义响应状态码 背景介绍开发环境开发步骤及源码工程目录结构背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费…...
Locksupport凭证的底层原理
LockSupport的凭证(通常称为“许可”或“permit”)的底层原理主要涉及到Java的Unsafe类以及系统级的线程同步机制。LockSupport是Java 6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语,其核心功能是通…...

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

对称密码学
1. 使用OpenSSL 命令行 在 Ubuntu Linux Distribution (发行版)中, OpenSSL 通常可用。当然,如果不可用的话,也可以使用下以下命令安装 OpenSSL: $ sudo apt-get install openssl 安装完后可以使用以下命令检查 OpenSSL 版本&am…...
正则表达式优化建议
文章目录 优化正则表达式代码示例:注意事项: 一些常见的正则表达式陷阱 优化正则表达式是提高文本处理效率和准确性的重要步骤。以下是一些优化正则表达式的方法: 以下是整理归纳后的正则表达式优化技巧: 优化正则表达式 一、预…...

Oracle RAC关于多节点访问同一个数据的过程
一、说明 Oracle RAC 存在多个计算节点,但是使用的共享存储。那么多个节点共同访问同一个资源,怎么保证一致性。 白文的逻辑理解简述: 用户1访问rac1 ,通过rac1获取AA数据块后,会加上latch锁。用户2通过rac2访问AA数据…...
IPC$漏洞多位密码爆破方法
虽然不应该将其用于非法的密码破解行为,但从代码修改角度来说,如果要破解多位密码(比如 n 位),你可以按照以下方式调整: 破解多位纯数字密码 如果你想破解 6 位纯数字密码: FOR /L %%i IN (100000,1,999999) DO (net use \\target - ip\ipc$ /user:weak %%i &&…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...