【C++模拟实现】map、set容器的模拟实现

【C++模拟实现】map、set容器的模拟实现

目录

  • 【C++模拟实现】map、set容器的模拟实现
      • map、set模拟实现的代码(insert部分)
        • 部分一:红黑树的迭代器以及红黑树
        • 部分二:对set进行封装
        • 部分三:对map进行封装
      • 遇到的问题以及解决方案

作者:爱写代码的刚子

时间:2023.9.17

前言:本篇博客有关map、set的模拟实现,其底层采用了红黑树的结构,记录了模拟实现时的问题和解决方案。


map、set模拟实现的代码(insert部分)

部分一:红黑树的迭代器以及红黑树

enum Colour
{BLACK,RED
};///红黑树的节点///
template<class T>
struct RBTreeNode
{RBTreeNode(const T& kv):_left(nullptr),_parent(nullptr),_right(nullptr),_c(RED),_kv(kv){}RBTreeNode* _left;RBTreeNode* _parent;RBTreeNode* _right;Colour _c; T _kv;};//红黑树的迭代器类
template<class T,class Ref,class Ptr>
class __iterator
{typedef RBTreeNode<T> Node;typedef __iterator<T,Ref,Ptr> Self;typedef __iterator<T,T&,T*> Iterator;
public:__iterator(Node* it):_it(it){}//如果这个对象是const迭代器,实现的是普通迭代器转为const迭代器,//如果这个对象是普通迭代器,实现的是普通迭代器的拷贝构造。__iterator(const Iterator& t):_it(t._it){}Ref operator*(){return _it->_kv;}Ptr operator->(){return &_it->_kv;}Self& operator++()//前置++{if(_it->_right){Node* subLeft=_it->_right;while(subLeft->_left){subLeft=subLeft->_left;}_it=subLeft;}else{Node* cur = _it;Node* parent=_it->_parent;while(parent&&parent->_right==cur){cur=cur->_parent;parent=cur->_parent;}_it=parent;}return *this;}Self operator++(int)//后置++{__iterator tmp(_it);if(_it->_right){Node* subLeft=_it->_right;while(subLeft->_left){subLeft=subLeft->_left;}_it=subLeft;}else{Node* cur = _it;Node* parent=_it->_parent;while(parent&&parent->_right==cur){cur=cur->_parent;parent=cur->_parent;}_it=parent;}return tmp;}
///对--的重载暂时不实现(思路和++相反,将operator++中的_left换成_right,将_right换成_left即可)bool operator!=(const Self& l) const{return _it!=l._it;}bool operator==(const Self& l) const{return _it==l._it;}Node* _it;
};
//红黑树的模拟实现
template<class K,class T,class SetKeyOfT>
struct RBTree
{typedef RBTreeNode<T> Node;typedef __iterator<T,T&,T*> iterator;typedef __iterator<T,const T&,const T*> const_iterator;RBTree():_root(nullptr){}
//红黑树迭代器begin和end///iterator begin(){Node* cur=_root;if(!_root){return nullptr;}while(cur->_left){cur=cur->_left;}return iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* cur=_root;if(!_root){return nullptr;}while(cur->_left){cur=cur->_left;}return const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}Node* Find(const K& key){Node* cur = _root;SetKeyOfT k;while (cur){if (k(cur->_data) < key){cur = cur->_right;}else if (k(cur->_data) > key){cur = cur->_left;}else{return cur;}}return nullptr;}pair<iterator,bool> insert(const T& kv){if(_root==nullptr){_root=new Node(kv);_root->_c=BLACK;return make_pair(iterator(_root),true);}Node* cur=_root;Node* parent=nullptr;SetKeyOfT k;while(cur){if(k(cur->_kv)>k(kv)){parent=cur;cur=cur->_left;}else if(k(cur->_kv)<k(kv)){parent=cur;cur=cur->_right;}else{return make_pair(iterator(cur),false);}}cur=new Node(kv);Node* newnode = cur;/if(k(parent->_kv)>k(kv)){parent->_left=cur;}else{parent->_right=cur;}cur->_parent=parent;//需要调整的情况while(parent&&parent->_c==RED){Node* grandfather=parent->_parent;if(grandfather->_left==parent){Node* uncle=grandfather->_right;//判断uncle的几种情况if(uncle&&uncle->_c==RED){uncle->_c=parent->_c=BLACK;grandfather->_c=RED;cur=grandfather;parent=cur->_parent;}else {if(cur==parent->_left){_RotateR(grandfather);grandfather->_c=RED;parent->_c=BLACK;}else{_RotateL(parent);_RotateR(grandfather);grandfather->_c=RED;cur->_c=BLACK;}break;}}else{Node* uncle=grandfather->_left;if(uncle&&uncle->_c==RED){uncle->_c=parent->_c=BLACK;grandfather->_c=RED;cur=grandfather;parent=cur->_parent;}else {if(cur==parent->_right){_RotateL(grandfather);parent->_c=BLACK;grandfather->_c=RED;}else{_RotateR(parent);_RotateL(grandfather);cur->_c=BLACK;grandfather->_c=RED;}break;}}}_root->_c=BLACK;return make_pair(iterator(newnode),true);}void _RotateR(Node* parent){Node*cur=parent->_left;Node*curRight=cur->_right;Node*ppnode=parent->_parent;cur->_right=parent;parent->_left=curRight;if(curRight){curRight->_parent=parent;}parent->_parent=cur;//处理ppnodeif(parent==_root){_root=cur;cur->_parent=nullptr;}else{if(ppnode->_left==parent){ppnode->_left=cur;}else{ppnode->_right=cur;}cur->_parent=ppnode;}}void _RotateL(Node* parent){Node* cur=parent->_right;Node* curLeft=cur->_left;Node* ppnode=parent->_parent;cur->_left=parent;parent->_right=curLeft;if(curLeft){curLeft->_parent=cur;}parent->_parent=cur;if(parent==_root){_root=cur;cur->_parent=nullptr;}else{if(ppnode->_left==parent){ppnode->_left=cur;}else{ppnode->_right=cur;}cur->_parent=ppnode;}  }Node* _root;
};

部分二:对set进行封装

template<class K>
class set
{struct SetKeyOfT{const K& operator()(const K& key)//仿函数{return key; }};
public:typedef typename RBTree<K,K,SetKeyOfT>::const_iterator iterator;typedef typename RBTree<K,K,SetKeyOfT>::const_iterator const_iterator;iterator begin() {return _t.begin();}iterator end() {return _t.end();}const_iterator begin() const{return _t.begin();}const_iterator end() const{return _t.end();}pair<iterator,bool> insert(const K& key){pair<typename RBTree<K,K,SetKeyOfT>::iterator,bool> ret= _t.insert(key);//这边我们需要注意,右边是普通迭代器,左边是const迭代器,我们需要实现普通迭代器构造const迭代器return pair<iterator,bool>(ret.first,ret.second);}private:RBTree<K,K,SetKeyOfT> _t;};

部分三:对map进行封装

template<class K,class V>
class map
{struct MapKeyOfT{const K& operator()(const pair<K,V>& kv){return kv.first;}};
public:typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::iterator iterator;typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin()  const{return _t.begin();}const_iterator end() const{return _t.end();}pair<iterator,bool> insert(const pair<K,V>& kv){return _t.insert(kv);}V& operator[](const K&key){pair<iterator,bool> ret = insert(make_pair(key,V()));return ret.first->second;}private:RBTree<K,pair<const K,V>,MapKeyOfT> _t;
};

遇到的问题以及解决方案

  • 【问题】:由于map和set共用一颗树的结构,但是传入的数据类型并不相同,set直接插入key,而map要插入pair。在树里面全部采用统一的模版参数T。在insert的比较大小环节会出现错误,pair不能直接进行大小的比较(虽然库里面pair有进行大小比较的重载函数但是效果并不符合预期)

    【解决】:在set和map的结构体里写一个仿函数,在树里面调用名称相同但是功能不同的仿函数,达到对数据的统一比较的效果。

    map:

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

    set:

在这里插入图片描述

在这里插入图片描述

红黑树:

在这里插入图片描述

在这里插入图片描述

红黑树中的SetKeyOfT可以是其他名字,不要和set中的SetKeyOfT搞混了。

  • 【问题】:普通迭代器如何构造const迭代器?

    由于set不能对数据进行修改,所以set的迭代器的下面使用红黑树的const迭代器实现的:

    在这里插入图片描述

    观察set对insert函数的封装:
    在这里插入图片描述

​ 由于set调用红黑树的insert函数而红黑树中insert函数默认返回iterator迭代器,而```pair<iterator,bool> insert(const K& key)``使用的是set中对红黑树中const迭代器封装后的迭代器,insert的返回值和该函数的返回值不相同,需要进行转换,由于红黑树中的迭代器没有实现转换就会出现以下报错:

在这里插入图片描述

【解决】:在红黑树的迭代器中实现普通迭代器对const迭代器的转换:

在这里插入图片描述

  • 【问题】:如何实现map的pair中的key不能被修改?

    【解决】:在这里插入图片描述

    注意!以下传入红黑树的模版参数中也需要加上const:在这里插入图片描述

  • 留意红黑树迭代器中的iterator++中的算法:

在这里插入图片描述

  • 在实现迭代器时,这两个运算符是一定要重载的:
    在这里插入图片描述

  • Map中对[]的重载方法:

    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stability AI推出Stable Audio;ChatGPT:推荐系统的颠覆者

&#x1f989; AI新闻 &#x1f680; Stability AI推出Stable Audio&#xff0c;用户可以生成个性化音乐片段 摘要&#xff1a;Stability AI公司发布了一款名为Stable Audio的工具&#xff0c;用户可以根据自己的文本内容自动生成音乐或音频。免费版可生成最长20秒音乐片段&a…

JL653—一个基于ARINC653的应用程序仿真调试工具

JL653是安装在PC机Windows操作系统上面的一层接插件&#xff0c;它能够真实地模拟ARINC653标准规定的功能性行为&#xff0c;从而可以供研发人员在PC机Windows环境下高效、快速的进行基于ARINC653的应用程序的开发、调试等。 JL653提供了ARINC 653 Part 1中要求的以下服务&…

手把手教你搭建农产品商城小程序:详细步骤解析

随着移动互联网的普及&#xff0c;越来越多的人开始关注如何在手机上进行购物&#xff0c;尤其是对于农产品这类日常生活所需品。本文将手把手教你搭建一个农产品商城小程序&#xff0c;让你轻松实现在手机上购买农产品的愿望。 一、登录乔拓云网后台 首先&#xff0c;我们需要…

ARM Linux DIY(十一)板子名称、开机 logo、LCD 控制台、console 免登录、命令提示符、文件系统大小

文章目录 前言板子名称uboot Modelkernel 欢迎词、主机名 开机 logoLCD 控制台console 免登录命令提示符文件系统大小 前言 经过前面十篇文章的介绍&#xff0c;硬件部分调试基本完毕&#xff0c;接下来的文章开始介绍软件的个性化开发。 板子名称 uboot Model 既然是自己的…

Lua学习笔记:在Visual Studio中调试Lua源码和打断点

前言 本篇在讲什么 调试Lua源码 本篇需要什么 对Lua语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠ 一级标题 &#x1f448; &…

HTTP状态码301(永久重定向)不同Web服务器的配置方法

文章目录 301状态码通常在那些情况下使用301永久重定向配置Nginx配置301永久重定向Windows配置IIS301永久重定向PHP下的301重定向Apache服务器实现301重定向 301重定向是否违反相关法规&#xff1f;推荐阅读 当用户或搜索引擎向服务器发出浏览请求时&#xff0c;服务器返回的HT…

Linux 企业级夜莺监控分析工具远程访问

目录 前言 1. Linux 部署Nightingale 2. 本地访问测试 3. Linux 安装cpolar 4. 配置Nightingale公网访问地址 5. 公网远程访问Nightingale管理界面 6. 固定Nightingale公网地址 前言 夜莺监控是一款开源云原生观测分析工具&#xff0c;采用 All-in-One 的设计理念&…

elasticsearch17-自动补全

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

【面试题】智力题

文章目录 腾讯1000瓶毒药里面只有1瓶是有毒的&#xff0c;问需要多少只老鼠才能在24小时后试出那瓶有毒。有两根不规则的绳子&#xff0c;两根绳子从头烧到尾均需要一个小时&#xff0c;现在有一个45分钟的比赛&#xff0c;裁判员忘记带计时器&#xff0c;你能否通过烧绳子的方…

[k8s] pod的创建过程

pod的创建过程 定义 Pod 的规范&#xff1a; apiVersion: v1 kind: Pod metadata:name: my-pod spec:containers:- name: my-containerimage: nginx:latest创建 Pod 对象&#xff1a; 使用 kubectl 命令行工具或其他客户端工具创建 Pod 对象&#xff1a; kubectl create -f…

线程锁(Thread Lock)和进程锁(Process Lock)

在Python中&#xff0c;线程锁&#xff08;Thread Lock&#xff09;和进程锁&#xff08;Process Lock&#xff09;具有相似的功能&#xff0c;但它们分别用于同步多线程和多进程环境中的资源访问。 进程锁 进程锁&#xff08;Process Lock&#xff09;可以用于在多进程环境中…

千巡翼X1 让航测无人机更小更轻更高效

利用无人机进行航空摄影测量&#xff0c;已成为测绘外业生产的主要方式&#xff0c;不仅方便快捷&#xff0c;更能全面准确获得成果。近年来&#xff0c;凭借快速高效、机动灵活、安全可靠、低成本等诸多优势&#xff0c;小型多旋翼无人机逐渐成为一些航测项目作业的新利器。 千…

新手如何开始Microstation CE版二次开发

一步步学习MicroStation CE MDL&#xff08;C&#xff09;开发 - 技术资料库 - Bentley 中国优先社区 - Bentley Communities https://communities.bentley.com/communities/other_communities/chinafirst/w/chinawiki/57704/microstation-ce-mdl-c一步步学习MicroStation CE A…

opencv 基础(持续更新中)

1 前言 https://www.couragesteak.com/ 2 安装 3 基础属性demo 打开一张图片&#xff1a; import cv2img cv2.imread(./girl.jpg)print(img.shape) # (1536, 1024, 3) 数组形状 print(type(img)) # numpy 数组 print(img) # 三维数组&#xff08;彩色图片&am…

产品经理学习笔记

产品文档之BRD、MRD和PRD - 知乎BRD、MRD和PRD一起被认为是从市场到产品需要形成的标准规范文档&#xff1a; 1、BRD&#xff08;Business Requirement Document&#xff09;&#xff0c;商业需求文档&#xff0c;是一份产品商业论证报告&#xff0c;基于商业目标或价值所描述的…

Python爬虫被封ip的解决方案

目录 一、网站反爬虫机制有哪些 二、Python爬虫被封ip的原因 三、爬虫被封IP怎么解决 四、代码示例 在爬虫程序运行过程中&#xff0c;被封禁IP地址是常见的问题之一。这通常是由于目标网站采取了反爬虫机制&#xff0c;例如限制单个IP地址的请求频率或识别请求特征等。当爬…

Python函数进阶:探索高级函数特性与技巧

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 Python中的函数不仅仅是…

【23种设计模式】组合模式【⭐】

个人主页&#xff1a;金鳞踏雨 个人简介&#xff1a;大家好&#xff0c;我是金鳞&#xff0c;一个初出茅庐的Java小白 目前状况&#xff1a;22届普通本科毕业生&#xff0c;几经波折了&#xff0c;现在任职于一家国内大型知名日化公司&#xff0c;从事Java开发工作 我的博客&am…

AjaxJavaScriptcss模仿百度一下模糊查询功能

1、效果 如下图所示&#xff0c;我们在输入大学时&#xff0c;程序会到后端查询名字中包含大学的数据&#xff0c;并展示到前端页面。 用户选择一个大学&#xff0c;该大学值会被赋值到input表单&#xff0c;同时关闭下拉表单&#xff1b; 当页面展示的数据都不符合条件时&…

【八大经典排序算法】堆排序

【八大经典排序算法】堆排序 一、概述二、思路解读三、代码实现&#xff08;大堆为例&#xff09; 一、概述 堆排序是J.W.J. Williams于1964年提出的。他提出了一种利用堆的数据结构进行排序的算法&#xff0c;并将其称为堆排序。堆排序是基于选择排序的一种改进&#xff0c;通…