C++【红黑树封装mapset】

news2024/11/16 11:35:07

文章目录

  • 前言
  • (一) 修改原红黑树
    • (1)逐步修改并调试运行
    • (2)红黑树的迭代器
    • (3)最后一步最重要的
  • 二、源代码压缩包

前言

我们在开始之前,首先要明白一点,前面我们实现模拟实现红黑树以及现在的封装map和set,我们的目的主要学习它库中的框架及模板设计和复用,不是要原模原样的去实现。

(一) 修改原红黑树

(1)逐步修改并调试运行

在这里插入图片描述
在这里插入图片描述

先看库中的代码,取了核心代码。我们可以看到kv搞出来value_type,就是相关pair,pair里面的key是const,这样就可以做到key不能修改,T可以修改,
再看里面的map里面的value是pair,再看set。在没看之前,我们可能会以为库中要用两颗红黑树,一颗存pair,一颗存key,pair封装map,key封装set,但是库中是通过泛型来设计的,共用了一颗红黑树。
在这里插入图片描述
看库中代码,set第二个模板参数传到了value,这个value又传到了节点里面,节点又传到节点结构体上面的模板参数value,再到结构里面的value,如图红线。也就是说set而言,红黑树里的节点的值是由第二个参数决定,第二个参数是K就是K,为什么要传第一个模板参数,它要和map复用,如蓝线。第一个模板参数拿到K的类型,因为查找,删除等接口的参数是K,第二个模板参数决定了树的节点存什么,是K还是KV。这里就是用模板参数来解决使用一颗红黑树的问题。其实底层还是两颗红黑树,只是模板帮我们完成,对模板而言,传不同的参数实例化不同的类,只是同一个类模板实例化的。

第一步:

在这里插入图片描述

我们一步一步加,这里就不叫value_type,自己写的时候这叫T。把节点结构体里面的pair类型改成T,构造函数里面用data初始化_data。以及后面涉及到pair<K,V>&kv的全都改成T&data。map就通过第二个模板参数控制node里面的data,同理set也一样。
之前是kv现在要改为_data,把kv.first改为_data,但是这里的第二个_data并不是我们想要的,因为这里的data到底是什么并不知道,上一层加了一层泛型。虽然可以编译通过,pair是可以比较大小的,如果first小就小,如果不小就看second,但是我们只期望按key去比较,value不参与,那就要用仿函数。
我看看库里的:
在这里插入图片描述

第四个在自己实现的时候就省掉意思就是手动控制这个key怎么比,第三个模板参数是把keyvalue里面的key取出来。红黑树那一层是不知道T是什么,但是调用它的那一层知道也就是Map.h和Set.h知道。所以只需要在Map.h和Set.h增加一个结构体里面写仿函数,在红黑树里面,传过来一个类KeyOf,在用到的地方定义一个对象kot,帮助我们把data的key取出来,对于set直接取key,对于map取出pair里面first,comapre是控制比较大小方式可以自己加,它就是可以控制比较方式如下图写的只是部分代码:
在这里插入图片描述
其实这就是配套使用,达到map和set公用了一套代码。可以看到stl里面的特点擅于用模板参数做复用。

(2)红黑树的迭代器

在这里插入图片描述
在这里插入图片描述

接下来最终还要的环节就是迭代器,在库里面set普通迭代器和const迭代器都是用的const迭代器,而map的普通迭代器是普通迭代器,因为map需要修改,需要修改它的value,不能修改K,所以它是通过pair里面const K达到的;而set不能修改,它只有K,K是不能修改的。
我们先写好大致的框架:如下图
在这里插入图片描述

我们用node构造这个迭代器,接下来就写迭代器的功能,诸如++,解引用等。然后在红黑树里面把begin和end写出来,begin返回的是中序第一个即红黑树的最左节点的迭代器,怎么返回迭代器,就是节点指针构造迭代器,end返回的不是最后一个数据,它是最后一个数据下一个位置,直接用空去构造。
接下来就是实现++
在这里插入图片描述

怎么加到下一个位置,如果第一个位置开始加:
如果右不为空,下一个就是右子树的的最左节点,然后再把节点的指针给给那个节点即_node;
如果右为空,要沿着到根的路径,找孩子是父亲左的那个祖先,这需要三叉链中的parent,直到parent的右不是cur停止,最后把parent给_node。
实现–
在这里插入图片描述

如果左不为空,就找左子树的最右节点。
如果左为空,就找孩子是父亲的右的那个祖先,直到parent的左不是cur停止,最后把parent给_node。
也就是说迭代器的封装屏蔽了底层的细节。
我们可以先跑一下:
在这里插入图片描述

在这里插入图片描述

这里打印map是it->返回时数据的指针,相当于时pair*,在来个箭头就能访问first和sceond,这里省略了,编译器会给我们加上。加typename原因是因为它不认识这到底是静态变量还是类型,只要在类模板取这个东西就加这个typename,告诉编译器这是个类型,等类模板实例化以后去找个东西。
我们还要在Map.h里面实现一个operator[]
如图:
在这里插入图片描述
[]主要针对map来实现的,它要去访问第二个V值也就是pair的second,如果它没有就插入,如果有就返回key所在节点的迭代器,因为里面调用的是insert,这样就不仅能查找对应的value值,并有修改功能并可以插入新的键值对。而且并把有关的insert返回值同样set也一样改为pair<iterator,bool>,里面是是一个迭代器和布尔值,如果返回成功就返回新插入节点的构造的迭代器加一个true即make_pair(itertaor(cur), true)。

(3)最后一步最重要的

先看图:
在这里插入图片描述
这颗树就不是搜索树,因为set不准别人修改,如果在typedef typename::RbTree <K, const K, SetKeyOf>::itertaor iterator;是可以,但是会存在各种各样的问题,而且红黑树里面typedef _RbTreeItrerator<T, T&, T*> itertaor; typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;第二个难道是const const K吗,这样做显然不行。
在前面我们说过,并在库里看到set的普通迭代器也是const迭代器,它其实就是为了解决这个问题,如图:
在这里插入图片描述
我们也这样改会出现以下的错误:
在这里插入图片描述
VS13和19的报错都是因为我们没写对应的构造
19的错误在用类模板进行编程,如果没有定义(自己写)一个拷贝构造函数,那就会以默认的类型为标准去构造,这里因为当被实例化const_iterator时候,它默认拷贝构造的参数也是const_iterator,而传过来的参数iterator,所以19编译才会那样的错误,虽然iterator和const_iterator底层调用的是一个类模板,在实例化时候会认为是不同的类型,所以在类模板中,不能像常规一样想一个A类型隐式类型转化const A类型,在泛型编程中,讲究要确保与模板匹配。
13的错误找到对应的构造没有找到,只找到了用节点指针的构造,编译器会以为用普通迭代器转换成这个节点指针回其实去匹配这个构造去了,但是迭代器不能转换为这个指针,所以会报错,它就说在const迭代器实现里面,参数不能转换为节点指针。

看stl库中的解决
因为_t是一个普通对象,这个普通对象调用的时普通的begin,普通的begin返回的是普通迭代器,而这个iterator就不是普通迭代器,const迭代器和普通迭代器是同一个类模板,但是它们是同一个类模板实例化的不同的模板参数,这里的问题本质上是类型转换。我们看一下库中的解决,
在这里插入图片描述

分析:
我们的begin和end和库中写的一样,说明它这里并没有做改变,但是红黑树里面的迭代器构造比我们多了一个拷贝构造函数,它这里的Self和Iterator定义是不同的,Self就是自己类型的typedef,Iterator是一个普通迭代器类型,而且第二个拷贝构造参数里面没有用Self,而是用了Iterator。
1、也就是说,当使用iterator作为传过来的参数,如果类模板被实例化为iterator:这个函数就是拷贝构造。,或着如果类模板被实例化为const_iterator:它就不再是拷贝,它是一个支持用普通iterator去构造初始化cont_iterator的构造函数,其实还是拷贝构造,只是说法而已。那支持了普通迭代器去构造const迭代器,_t.begin返回的是普通迭代器,而外面那一层的iterator是一个const迭代器,就支持了隐式类型转换,单参数的构造函数支持隐式类型转换,也就是支持普通到const迭代器的构造,其实就是权限的缩小
2、使用const_iterator作为传过来的参数,,如果类模板被实例化为iterator**,虽然调默认拷贝构造,会出现类型不匹配的错误;或着如果类模板被实例化为const_iterator,就会调用默认拷贝构造函数,因为我们自己写的拷贝构造里面的参数和它不匹配,所以才调自己生成的。

总结:也就是说it是一个普通对象,返回的是一个普通迭代器iterator,外面的iterator是const迭代器,就会涉及到一个问题,类型不一样,就会认为要去转换const迭代器,那两个不同类型转换可以依赖一个单参构造函数,支持一个隐式类型转换,就会找const迭代器的构造。如果我们不要普通版本只要const迭代器也可以解决,但是我们要和map复用,入宫这样map不可以,set不修改,但map里的value要修改,所以不能。

二、源代码压缩包

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文带你了解MySQL之事务隔离级别和MVCC

目录 一、数据准备二、事务隔离级别2.1 事务并发执行遇到的问题2.2 SQL标准中的四种隔离级别2.3 MySQL中支持的四种隔离级别 三、MVCC3.1 版本链3.2 ReadView3.2.1 READ COMMITTED3.2.2 REPEATABLE READ 3.3 MVCC小结 四、关于purge五、总结 一、数据准备 为了我们学习的顺利进…

一篇文章搞懂CMake(gcc、g++、cmake解释)

一篇文章搞懂CMake &#xff08;gcc、g、cmake解释&#xff09; 这里写目录标题 一篇文章搞懂CMake &#xff08;gcc、g、cmake解释&#xff09;gccgcmake1. CMake 流程如何使用cmake&#xff1f;简单的CMake.txt文件 参考 gcc gcc命令来自英文词组“GNU Compiler Collection”…

4.C++多线程-- unique_lock(类模板)

1.unique_lock 1. unique_lock<mutex> myUniLock(myMutex); 完全可以取代lock_guard 2. unique_lock 也可以使用----std::adopt_lock 3.使用adopt_lock&#xff0c;之前要先使用lock. 4.std::chrono::milliseconds my_sleepTime(20000)//20000毫秒 std::this_thread:…

运维小白必学篇之基础篇第十一集:系统进程实验

系统进程实验 实验作业&#xff1a; 1、利用top命令查看当前系统进程&#xff0c;要求没5秒刷新一次&#xff0c;分别按照CPU使用率&#xff0c;内存使用量&#xff0c;CPU使用时间&#xff0c;PID分别排序一次 每5秒刷新一次&#xff1a;top -d 5 按照CPU使用率排序&#xf…

【python】如何入门python?

文章目录 前言一、为什么学习Python&#xff1f;二、学习Python的前置条件三、入门Python的步骤3.1 安装Python3.2 安装Python开发环境3.3 学习Python基础语法3.4 编写你的第一个Python程序3.5 学习Python的高级特性3.6 使用Python库和框架 四、Python入门的资源五、常见的Pyth…

allure报告中如何让测试步骤更清晰

一、allure step测试用例步骤说明 allure step编写测试用例有两种方式 1、with allure.step()用在测试用例中 公共方法代码&#xff1a; # common_fucntion.py import allure import pytest from common.tools.log_util import LoggerrunlogLogger().get_log流程性的用例&a…

【FMC129】 基于JESD204B接口的8通道125MSPS 16位AD采集FMC子卡

板卡概述 FMC129是一款8通道125MHz采样率16位AD采集FMC子卡&#xff0c;符合VITA57.1规范&#xff0c;可以作为一个理想的IO模块耦合至FPGA前端&#xff0c;8通道AD通过高带宽的FMC连接器&#xff08;HPC&#xff09;连接至FPGA从而大大降低了系统信号延迟。 该板卡支持板上可编…

git commit之前,没有pull最新代码,导致无法push代码如何解决?——git三板斧

一、报错&#xff1a; 如果在 git commit 之前没有 pull 最新代码&#xff0c;再进行 push 操作可能会出现冲突&#xff0c;导致无法 push 代码。此时&#xff0c;git 会提示类似以下的错误信息&#xff1a; error: failed to push some refs to gitgithub.com:username/repo…

OSSIM进行主机漏洞扫描(03)

OSSIM进行主机漏洞扫描方式 按照如图选择&#xff0c;ENVIRONMENT–SCAN JOBS–NEW SCAN JOB进入新增页面 其中各选项含义如下 Job Name:扫描任务名称。 Select Sensor:扫描的嗅探器。 Profile:扫描的类型&#xff0c;包括Deep- Non destructive Full and Slow scan(深入)、D…

【完整版】2023二级建造师《建筑实务》真题答案解析(2天考3科)

2023二级建造师考试将在6月3日、4日举行&#xff0c;2023二建《市政实务》考试时间&#xff08;2天考3科&#xff09;&#xff1a;6月4日 9:00-12:00&#xff0c; 考后甘建二将及时发布2023年二建市政实务真题及答案解析&#xff0c;敬请关注 2天考3科地区&#xff1a;四川、山…

1个月1000家店,库迪起飞

5月30日最新消息&#xff0c;库迪咖啡第3000家门店在北京开业。 从2022年10月到4月&#xff0c;半年开了2000家。4月到5月更夸张&#xff0c;1个月开了一千家店。 这就是库迪速度&#xff0c;线下疯狂扩张&#xff0c;线上也没闲着。 今天在抖音本地生活热销榜&#xff0c;大…

华为OD机试题【导师请吃火锅】【2023 B卷 100分】

文章目录 &#x1f3af; 前言&#x1f3af; 题目描述&#x1f3af; 解题思路&#x1f4d9; Python实现代码&#x1f4d7; Java实现代码&#x1f4d8; C语言实现&#xff1a; &#x1f3af; 前言 &#x1f3c6; 《华为机试真题》专栏含2023年牛客网面经、华为面经试题、华为OD机…

5 从Win32过度到MFC

文章目录 配置设置建立MFC工程的三部曲1.建立CWinApp 类的派生类2.重写CWinApp3.定义CWinApp派生类的全局变量全部代码 添加对话框资源创建窗口类继承CDialog 导入资源文件修改资源文件ID加载资源文件 全部代码 配置设置 建立MFC工程的三部曲 1.建立CWinApp 类的派生类 class…

四、初探[ElasticSearch]集群架构原理与搜索技术

目录 一、浅析Elasticsearch架构原理1.Elasticsearch的节点类型1.1 Master节点1.2DataNode节点 二、分片和副本机制2.1分片2.2副本2.3指定分片、副本数量2.4查看分片、主分片、副本分片 三、Elasticsearch工作流程3.1Elasticsearch文档写入原理3.2Elasticsearch检索原理 四、El…

Java开发手册中为什么要求三目运算符必须要注意类型对齐

场景 java开发手册中对于三目运算符的使用要求如下: 【强制】三目运算符 condition? 表达式 1 : 表达式 2 中&#xff0c;高度注意表达式 1 和 2 在类型对齐时&#xff0c; 可能抛出因自动拆箱导致的 NPE 异常。 说明&#xff1a;以下两种场景会触发类型对齐的拆箱操作&am…

华为路由器 NAT 配置

拓扑图 静态 NAT 静态地址转换是指外部网络和内部网络之间的地址映射关系由配置确定&#xff0c;该方式适用于内部网络与外部网络之间存在固定访问需求的组网环境。静态地址转换支持双向互访&#xff1a;内网用户可以主动访问外网&#xff0c;外网用户也可以主动访问内网。 一…

2023国际管理会计教育联盟发展论坛在沪成功召开

2023年5月7日&#xff0c;由教育部中外人文交流中心、国际管理会计教育联盟&#xff08;下称“联盟”&#xff09;主办&#xff0c;中国商业会计学会、上海交通大学安泰经济与管理学院承办的2023国际管理会计教育联盟发展论坛&#xff08;下称“发展论坛”&#xff09;在上海成…

第4章:SpringMVC的域对象共享数据

1、使用ServletAPI向request域对象共享数据 ①控制器 Controller public class HelloController {RequestMapping("/test")public String index(HttpServletRequest request){request.setAttribute("test","test666");return "index"…

避坑之网上下单的手机流量卡为什么老是失败!

最近有一些小伙伴们反应&#xff1a;在网上下单的手机流量卡&#xff0c;经常提示下单失败&#xff0c;不是这问题就是那问题的。 对于小伙伴们的诉求&#xff0c;小编向来是来者不拒的。今天就为大家整理一下下单失败的四大原因。 失败原因一&#xff1a;下单信息填写错误 下…

javax.validation常用注解

javax.validation 提供了一系列的注解&#xff0c;用于在 Java Bean 中对属性进行验证&#xff0c;主要有以下几种&#xff1a; NotNull&#xff1a;验证对象不可为 null&#xff1b;NotEmpty&#xff1a;验证字符串&#xff0c;数组&#xff0c;Collection&#xff0c;Map不可…