【C++】适配器· 优先级队列 仿函数 反向迭代器

news/2024/5/19 14:49:24

目录

  • 适配器:
  • 适配器的应用:
    • 1. 优先级队列:
      • 仿函数:
        • 更深入的了解仿函数:
        • 一个关于不容易被注意的知识点:
    • 2. 反向迭代器:(list为例)

适配器:

我们先来谈来一下容器适配器的概念:

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

在这里插入图片描述

适配器的应用:

1. 优先级队列:

仿函数我们暂时不提。
优先级队列其实就是我们的常说的堆。

那我们直接按照堆的方式进行创建一个优先级队列的类即可。
可以参考堆的博客

另外由于我们是采取适配器模式,于是可以直接在模版列表多加一个vector<T>的缺省,
可以理解为对已有的容器进行封装形成我们需要的容器。

注意:我们此时的代码实现的是大堆

	template<class T, class Container = vector<T>>class priority_queue{public:priority_queue(){}template<class InputIterator>priority_queue(InputIterator first, InputIterator end):con(first, end){for (int i = (con.size() - 2) / 2; i >= 0; i--){adjust_down(i);}}void adjust_up(int child){while (child > 0){int parent = (child - 1) / 2;if (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);adjust_up(con.size() - 1);}void adjust_down(int parent){int 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 = child * 2 + 1;}}void pop(){swap(con[0], con[con.size() - 1]);con.pop_back();adjust_down(0);}const T& top(){return con[0];}size_t size(){return con.size();}bool empty(){return con.size() == 0;}private:Container con;};

在另外说一下,对于迭代器区间构造我们选择使用向上调整算法,这是因为向上调整算法建堆是要优于向下建堆的
在这里插入图片描述

仿函数:

但是我们在这里还有一个问题,每次我们想改变大堆或者小堆时,都要进行对代码的修改,再C语言中我们可以使用函数指针来解决,qsort就是一个很好的例子。
在这里插入图片描述
使用函数指针进行升序还是降序的选择,我们的C++当然也可以选择这样做,但是C++并不喜欢指针,于是应时而生一个仿函数

什么是仿函数?
在这里插入图片描述
通过上图我们就可以得出一个结论:
当类中重载()操作符时即可称之为仿函数。

那我们现在就可以使用仿函数随心所欲的更改大堆还是小堆了

	template<class T>struct Less{bool operator()(const T& a, const T& b){return a < b;}};template<class T>struct Greater{bool operator()(const T& a, const T& b){return a > b;}};template<class T, class Container = vector<T>, class Compare = Less<T>>class priority_queue{public:priority_queue(){}template<class InputIterator>priority_queue(InputIterator first, InputIterator end):con(first, end){for (int i = (con.size() - 2) / 2; i >= 0; i--){adjust_down(i);}}void adjust_up(int child){while (child > 0){Compare com;int parent = (child - 1) / 2;if (com(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);adjust_up(con.size() - 1);}void adjust_down(int parent){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]);}parent = child;child = child * 2 + 1;}}void pop(){swap(con[0], con[con.size() - 1]);con.pop_back();adjust_down(0);}const T& top(){return con[0];}size_t size(){return con.size();}bool empty(){return con.size() == 0;}private:Container con;};

到这也就实现了与库中类似的优先级队列。

库中less实现的是小堆greater是大堆,我们这的实现与库中保持一致。

更深入的了解仿函数:

我们已经掌握了仿函数的初阶用法

假设我们现在有一个Date类

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool 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 operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
private:int _year;int _month;int _day;
};

我们使用优先级队列对3个日期对象进行排序:

int main()
{cyc::priority_queue<Date> pq;Date d1(2000, 2, 20);pq.push(d1);pq.push(Date(2000, 2, 21));pq.push({2000, 2, 22});while (!pq.empty()){cout << pq.top() << endl;pq.pop();}cout << endl;return 0;
}

我们在这里使用了3中传参方式,有名对象,匿名对象,初始化列表传参。


一个关于不容易被注意的知识点:

匿名对象其实是具有const属性哦!

class A
{
private:int a;int b;
};int main()
{const A& ref = A();//匿名对象是const对象。return 0;
}

不加const会报错。

但是我们const自定义类型对象也可以调用非const成员函数

class A
{
public:void print(){}
private:int a;int b;
};int main()
{const A& ref = A();//匿名对象是const对象。A().print();return 0;
}

这是经常不被注意的一个点,也算是编译器对于某些场景的特殊处理。


不过到现在这也仅仅是我们已经掌握的知识,如果我们传入的是Date类型的指针呢?
在这里插入图片描述
答案依旧如我们所料,传入指针,那么指针是内置类型,肯定就直接对只怎的大小进行比较,那我们如何解决这个问题?

操作符重载可以吗?
答案是不可以的,不可重载内置类型,故排除此方案。

那么仿函数?
答案是肯定可以的。
如何使用仿函数进行解决?

struct PDateLess
{bool operator()(const Date* left, const Date* right){return *left < *right;}
};

给模版类型时我们给自己实现的仿函数即可!

	cyc::priority_queue<Date*, vector<Date*>, PDateLess> pq;Date* pd1 = new Date(2000, 2, 20);Date* pd2 = new Date(2000, 2, 21);Date* pd3 = new Date(2000, 2, 22);pq.push(pd1);pq.push(pd2);pq.push(pd3);while (!pq.empty()){cout << *pq.top() << endl;pq.pop();}

所以,我们可以自定义仿函数行为!

到这里不知道有没有细心的小伙伴发现,我们有时候模版给的是类型,有时给的是对象

就像优先级队列与sort库函数。
本质的区别是因为一个是类模版,一个是函数模版!
在这里插入图片描述

2. 反向迭代器:(list为例)

我们在来进阶的看一下适配器,
适配器是将一个容器封装成我们需要的容器

stl库实现反向迭代器的思路是不是在重新写一个反向迭代器类,而是利用普通迭代器封装为反向迭代器!
他们之间是一个上下层的关系!
而const迭代器与普通迭代器是一个平行的关系!

首先如果我们自己利用如上思路进行实现的话。

大概率如下图代码一样,这样写的话,注意我们的重载*的返回值应该怎么写?

template<class Iterator>
struct ReverseIterator
{typedef ReverseIterator<Iterator> self;Iterator rit;ReverseIterator(Iterator it){rit(it);}self& operator++(){rit--;return *this;}self& operator--(){rit++;return *this;}//返回值怎么写?operator*(){return *rit;}
};

为了解决这个问题我们可以再引入模版参数,与实现const时的解决方案相似。

#pragma oncetemplate<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{typedef ReverseIterator<Iterator, Ref, Ptr> self;Iterator rit;ReverseIterator(Iterator it){rit(it);}self& operator++(){rit--;return *this;}self& operator--(){rit++;return *this;}Ref operator*(){return *rit;}Ptr operator->(){return rit.operator->();}bool operator!=(self it){return rit != it.rit;}
};

再者,库中rbegin与rend是与我们begin与end的位置是直接翻转的在这里插入图片描述
讲究的就是一个对称。
那么这样进行打印的话就会打印出未知数。
我们期望的是4 3 2 1
在这里插入图片描述
解决方法也很简单,对*重载进行一下修改即可。
在这里插入图片描述
本篇文章就到此结束了,欢迎询问。


http://www.mrgr.cn/p/75172270

相关文章

Hive 解决数据倾斜方法

数据倾斜问题&#xff0c; 通常是指参与计算的数据分布不均&#xff0c; 即某个 key 或者某些 key 的数据量远超其他 key&#xff0c; 导致在 shuffle 阶段&#xff0c; 大量相同 key 的数据被发往同一个 Reduce&#xff0c; 进而导致该 Reduce 所需的时间远超其他 Reduce&…

vis.js性能折线图

代码案例<!doctype html> <html> <head><title>Timeline</title><script type="text/javascript" src="https://unpkg.com/vis-timeline@latest/standalone/umd/vis-timeline-graph2d.min.js"></script><scr…

计算请假时间,只包含工作时间,不包含中午午休和非工作时间及星期六星期天,结束时间不能小于开始时间

1.计算相差小时&#xff0c;没有休息时间 computed: {// 计算相差小时time() {let time 0;if (this.ruleForm.date1 &&this.ruleForm.date2 &&this.ruleForm.date3 &&this.ruleForm.date4) {// 开始时间let date1 this.ruleForm.date1;let y date…

设计一个算法删除单链表L(有头节点)中的一个最小值结点

数据结构 链表 笔试题:设计一个算法删除单链表L(有头节点)中的一个最小值结点。/***************************************************************** * * file name : linkedlist.c * author : cnzycwp@126.com * data : 2024/04/22 * function : 删除单链表中的一…

docker服务无法启动

背景&#xff1a;断电重启经常会导致磁盘io错误&#xff0c;甚至出现磁盘坏块 这时可以使用xfs_repair来修复磁盘&#xff0c;但是修复过程可能会导致部分数据丢失 xfs_repair -f -L /dev/sdc问题一&#xff1a; Apr 15 19:27:15 Centos7.6 systemd[1]: Unit docker.service e…

Visual Studio 2022 Professional、Enterprise安装教程

Visual Studio 2022 Professional、Enterprise安装教程 下载安装包安装 我是电脑已经有VS2019&#xff0c;现在加装一个VS2022。 下载安装包 首先下载安装包&#xff0c;进入官网进行下载&#xff0c;VS官网下载地址。 进入之后&#xff0c;会显示如下界面&#xff0c;选择Pro…

linux-rpm包管理-命名-管理

1.RPM基础概述 RPM全称 RPM Package Manager 缩写,由红帽开发用于软件包的安装,升级卸载与查询 为什么要学rpm就像在windows系统中一样,如果你想要安装一个 QQ ,安装一个 微信 ,安装一款 游戏 ,首先要去该软件的官网上去下载相关的软件包,通常都是 .exe 的安装包。还有那…

力扣110. 平衡二叉树

思路&#xff1a;与二叉树最大高度类似&#xff0c;但是这里需要返回 -1 的高度来标识不是平衡二叉树&#xff0c;判断左右子树的高度相差大于1则不平衡&#xff0c;否则就是平衡。 class Solution {public boolean isBalanced(TreeNode root) {int ans func(root);if(ans >…

最强开源大模型Meta LIama3抢先在线体验!

4月19日Facebook母公司Meta重磅推出了其迄今最强大的开源人工智能&#xff08;AI&#xff09;模型——Llama 3。模型分为两种规模&#xff1a;8B 和 70B 参数&#xff0c;每种规模都提供预训练基础版和指令调优版。最强开源大语言模型Meta LIama3可以在线体验啦&#xff01; G…

【运输层】TCP 的流量控制和拥塞控制

目录 1、流量控制 2、TCP 的拥塞控制 &#xff08;1&#xff09;拥塞控制的原理 &#xff08;2&#xff09;拥塞控制的具体方法 1、流量控制 一般说来&#xff0c;我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快&#xff0c;接收方就可能来不及接收&#x…

TensorFlow文件读取 --TFRecords文件

TFRecords文件 是一种二进制文件&#xff0c;能够很好的利用内存&#xff0c;更方便复制和移动&#xff0c;并且不需要单独的标签文件 使用步骤 1&#xff09;获取数据 2&#xff09;将数据填入到Example协议内存块&#xff08;protocol buffer&#xff09; 3&#xff09;将协…

vis.js本地化折线图

代码案例<!doctype html> <html> <head><title>Timeline</title><script type="text/javascript" src="https://unpkg.com/vis-timeline@latest/standalone/umd/vis-timeline-graph2d.min.js"></script><lin…

【每周例题】力扣 C++ 分割字符串

分割字符串 题目 题目分析 1.先确定用容器存储,容器的存储结构如下图所示: 2.这个题目的话,第一反应应该是用到动态规划,下面是动态规划的模板:res = [] ans = []def backtrack(未探索区域, res, path):if 未探索区域满足结束条件:res.add(ans) # 深度拷贝returnfor 选择 …

Midjourney 实现角色一致性的新方法

AI 绘画的奇妙之处&#xff0c;实乃令人叹为观止&#xff01;就像大千世界中&#xff0c;寻不见两片完全相同的树叶一般&#xff0c;AI 绘画亦复如是。同一提示之词&#xff0c;竟能催生出千变万化的图像&#xff0c;使得AI所绘之作&#xff0c;宛如自然之物般独特&#xff0c;…

内网信息收集命令汇总

查看网络配置信息 ipconfig/all查看操作系统及软件信息查看操作系统和版本信息 systeminfo | findstr /B /C:"OS"查看系统体系结构 echo %PROCESSOR_ARCHITECTURE%查看安装的软件及版本、路径等 wmic product get name, versionpowershell "Get-WmiObject -clas…

上位机图像处理和嵌入式模块部署(智能硬件的介绍)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前&#xff0c;用上位机软件虽然可以部署项目&#xff0c;但是它本身有自己的缺点&#xff0c;那就是稳定性差、价格贵。稳定性这部分&#xff0…

操作系统——进程

进程定义 是计算机中已经运行的程序是系统进行资源分配和调度的一个独立单位。 进程的特性 独立性&#xff1a;进程在内存中可以独立寻址&#xff0c;每个进程都有一个独立的堆栈空间。动态性&#xff1a;进程在执行过程中可以申请资源、使用资源、释放资源。并发性&#xf…

Nacos配置管理-微服务配置拉取

创造来源&#xff1a;在学习微服务这部分内容的时候遇到很多bug&#xff0c;改了又改&#xff0c;最后改好了。以下是我修改后实现配置拉取的代码&#xff0c;这里我使用了鉴权&#xff0c;所以配置里面有使用到下面的代码&#xff0c;如果没有配置鉴权则删掉下面代码。新版本的…