C++【红黑树封装map&&set】

这篇具有很好参考价值的文章主要介绍了C++【红黑树封装map&&set】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

(一) 修改原红黑树

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

C++【红黑树封装map&&set】
C++【红黑树封装map&&set】

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

第一步:

C++【红黑树封装map&&set】

我们一步一步加,这里就不叫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不参与,那就要用仿函数。
我看看库里的:
C++【红黑树封装map&&set】

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

(2)红黑树的迭代器

C++【红黑树封装map&&set】
C++【红黑树封装map&&set】

接下来最终还要的环节就是迭代器,在库里面set普通迭代器和const迭代器都是用的const迭代器,而map的普通迭代器是普通迭代器,因为map需要修改,需要修改它的value,不能修改K,所以它是通过pair里面const K达到的;而set不能修改,它只有K,K是不能修改的。
我们先写好大致的框架:如下图
C++【红黑树封装map&&set】

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

怎么加到下一个位置,如果第一个位置开始加:
如果右不为空,下一个就是右子树的的最左节点,然后再把节点的指针给给那个节点即_node;
如果右为空,要沿着到根的路径,找孩子是父亲左的那个祖先,这需要三叉链中的parent,直到parent的右不是cur停止,最后把parent给_node。
实现–
C++【红黑树封装map&&set】

如果左不为空,就找左子树的最右节点。
如果左为空,就找孩子是父亲的右的那个祖先,直到parent的左不是cur停止,最后把parent给_node。
也就是说迭代器的封装屏蔽了底层的细节。
我们可以先跑一下:
C++【红黑树封装map&&set】

C++【红黑树封装map&&set】

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

(3)最后一步最重要的

先看图:
C++【红黑树封装map&&set】
这颗树就不是搜索树,因为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迭代器,它其实就是为了解决这个问题,如图:
C++【红黑树封装map&&set】
我们也这样改会出现以下的错误:
C++【红黑树封装map&&set】
VS13和19的报错都是因为我们没写对应的构造
19的错误在用类模板进行编程,如果没有定义(自己写)一个拷贝构造函数,那就会以默认的类型为标准去构造,这里因为当被实例化const_iterator时候,它默认拷贝构造的参数也是const_iterator,而传过来的参数iterator,所以19编译才会那样的错误,虽然iterator和const_iterator底层调用的是一个类模板,在实例化时候会认为是不同的类型,所以在类模板中,不能像常规一样想一个A类型隐式类型转化const A类型,在泛型编程中,讲究要确保与模板匹配。
13的错误找到对应的构造没有找到,只找到了用节点指针的构造,编译器会以为用普通迭代器转换成这个节点指针回其实去匹配这个构造去了,但是迭代器不能转换为这个指针,所以会报错,它就说在const迭代器实现里面,参数不能转换为节点指针。

看stl库中的解决
因为_t是一个普通对象,这个普通对象调用的时普通的begin,普通的begin返回的是普通迭代器,而这个iterator就不是普通迭代器,const迭代器和普通迭代器是同一个类模板,但是它们是同一个类模板实例化的不同的模板参数,这里的问题本质上是类型转换。我们看一下库中的解决,
C++【红黑树封装map&&set】

分析:
我们的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要修改,所以不能。

二、源代码

红黑树源代码文章来源地址https://www.toymoban.com/news/detail-468420.html

到了这里,关于C++【红黑树封装map&&set】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++ | 红黑树以及map与set的封装

    目录 前言 一、红黑树 1、红黑树的基本概念 2、红黑树相关特性 3、红黑树结点的定义 4、红黑树的查找 5、红黑树的插入 6、二叉树的拷贝构造与析构 7、红黑树的检测 8、红黑树总结 二、map与set的封装 1、红黑树的结点 2、红黑树迭代器 3、set的封装 4、map的封装 三、源码仓库

    2024年02月14日
    浏览(37)
  • 【C++】map与set容器——红黑树底层封装

    💭STL中,容器大概可分为两种类型——序列式容器和关联式容器。在前面的系列文章中,我们已经介绍了诸多序列式容器,如:vector、list、stack、queue等,它们以序列的形式存储数据。 💭而关联式容器也是一种非常重要的容器。标准的STL关联式容器分为set(集合)和map(映

    2023年04月11日
    浏览(40)
  • 【C++】使用红黑树进行封装map和set

    🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘 。 🛸 C++专栏 : C++内功修炼基地 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真

    2024年02月07日
    浏览(46)
  • 【C++】用红黑树迭代器封装map和set

    封装有点难 - . - 文章目录 前言 一、红黑树原先代码的修改 二、红黑树迭代器的实现 总结 因为我们要将红黑树封装让map和set使用,所以我们要在原来的基础上将红黑树代码进行修改,最主要的是修改模板参数,下面我们直接进入正题: 首先我们拿出STL中的源代码,看看大佬

    2024年02月06日
    浏览(42)
  • 【C++】用一棵红黑树同时封装出map和set

    苦厄难夺凌云志,不死终有出头日。 1. 在源码里面,对于map和set的实现,底层是用同一棵红黑树封装出来的,并不是用了两棵红黑树,一个红黑树结点存key,一个红黑树结点存key,value的键值对,这样的方式太不符合大佬的水准了,实际上在红黑树底层中用了一个模板参数Va

    2023年04月13日
    浏览(43)
  • Learning C++ No.23【红黑树封装set和map】

    北京时间:2023/5/17/22:19,不知道是以前学的不够扎实,还是很久没有学习相关知识,对有的知识可以说是遗忘了许多,以该篇博客有关知识为例,我发现我对迭代器和模板的有关知识的理解还不够透彻,不知道是对以前知识的遗忘,还是现在所学确实有难度,反正导致我很懵

    2024年02月05日
    浏览(50)
  • 【C++】STL——用一颗红黑树封装出map和set

    我们都知道set是K模型的容器,而map是KV模型的容器,但是它俩的底层都是用红黑树实现的,上篇博文中我们模拟实现了一颗红黑树,接下来将对其进行改造,继而用一颗红黑树封装出map和set。 本质上map和set其内部的主要功能都是套用了红黑树现成的接口,只是稍作改动即可

    2023年04月15日
    浏览(35)
  • AVL树,红黑树,红黑树封装map和set

    二叉搜索树虽可以缩短查找的效率,但如果 数据有序或接近有序二叉搜索树将退化为单支树 ,查找元素相当于在顺序表中搜索元素, 效率低下 。因此,咱们中国的邻居俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法: 当向二叉搜索树中插入

    2023年04月25日
    浏览(61)
  • 红黑树封装set和map(插入部分)

    之前我们实现了红黑树的插入的部分,本文主要介绍将之前实现的红黑树封装成map和set。我们是以学习的角度来封装容器,不用非要把库中容器所有功能都实现出来。我们主要目的是学习库中代码设计技巧和模板复用的思想。 我们在实现之前还是和以前一样去看看库中是怎么

    2024年02月07日
    浏览(40)
  • 【C++】红黑树 --- map/set 底层

    红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是 Red 或 Black . 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的;如下图: 每个结点不是红色就是黑色;

    2024年02月04日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包