【C++】模拟实现map和set

1. 调整之前实现的红黑树的insert

1.1 整体框架的搭建

新建两个头文件,Mymap.hMyset.h,一个源文件test.cpp,然后把之前实现的红黑树拷贝一份过来。

为了和库里面的一些东西区分开,我们还是把所有自己实现的内容都放在自己的命名空间里。

Mymap.h中搭建框架。key参数就⽤K,value参数就⽤V。

代码语言:javascript

AI代码解释

#include "RBTree.h" namespace lyj { template<class K, class V> class map { private: RBTree<K, pair<K, V>> _t; }; }

Myset.h中搭建框架。

代码语言:javascript

AI代码解释

#include "RBTree.h" namespace lyj { template<class K> class set { private: RBTree<K, K> _t; }; }

直接对这个RBTree.h进行修改。

首先就是把模板参数改掉,红⿊树中的数据类型我们使⽤T

代码语言:javascript

AI代码解释

template <class T> struct RBTreeNode { T _data; RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; Colour _col; RBTreeNode(const T& data) :_data(data) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) {} };

key参数就⽤K,这里的T就是决定到底是set还是map的。

如果我们传一个K过去就是set,传pair过去就是map。

1.2 insert的修改

因为我们把红⿊树中的数据类型T来表示了,也就是_data,这个_data是一个泛型,可能是set的K,可能是map的pair,用以前的逻辑就不能满足这个比较。

这个_kv换成_data也没用,不适用于set的K场景,并且pair自身支持的比较方法也不是我们想要的,我们需要任何时候都只比较K。

此时我们就需要实现一个仿函数。就是取出K来,set中就取K,map中就取first。

Myset.hset类里实现仿函数Set_Key_Of_T

代码语言:javascript

AI代码解释

struct Set_Key_Of_T { const K& operator()(const K& key) { return key; } };

set里这个仿函数就是给 key直接返回key就行。

Mymap.hmap类里实现仿函数Map_Key_Of_T

代码语言:javascript

AI代码解释

struct Map_Key_Of_T { const K& operator()(const pair<K, V>& kv) { return kv.first; } };

map的仿函数就是返回pair里的first。

有仿函数之后,红黑树的模板参数也要多加一个了,叫KeyOfT。

代码语言:javascript

AI代码解释

template<class K, class T, class KeyOfT> class RBTree { //... }

insert里的比较逻辑就要要成用这个仿函数写的逻辑。

map的find返回的是一个pair,这个pair的first是一个迭代器,second是一个bool值,所以这里的返回值也要修改一下。

代码语言:javascript

AI代码解释

pair<Iterator, bool> insert(const T& data) { }

插入成功,就返回新插入的值的迭代器和true,插入失败就返回已经存在的这个值的迭代器和false。所以整体代码如下。

代码语言:javascript

AI代码解释

pair<Iterator, bool> insert(const T& data) { if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK; //根节点为黑色 return make_pair(Iterator(_root, _root), true); } Node* parent = nullptr; Node* cur = _root; KeyOfT com; //仿函数 while (cur) { if (com(cur->_data) > com(data)) { parent = cur; cur = cur->_left; } else if (com(cur->_data) < com(data)) { parent = cur; cur = cur->_right; } else return make_pair(Iterator(cur, _root), false); } cur = new Node(data); Node* newnode = cur; //记录新插入的节点 cur->_col = RED; //新插入节点为红色 if (com(cur->_data) < com(parent->_data)) { parent->_left = cur; } else { parent->_right = cur; } cur->_parent = parent; while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; if (parent == grandfather->_left) //u在右 { Node* uncle = grandfather->_right; if (uncle && uncle->_col == RED) //u存在且为红 { parent->_col = BLACK; //u和p变黑 uncle->_col = BLACK; grandfather->_col = RED;//g变红 cur = grandfather; //继续向上更新 parent = cur->_parent; } else //u不存在 或 u存在且为黑 { if (cur == parent->_left) //单旋 { rotateR(grandfather);//以g为旋转点右旋 parent->_col = BLACK; //变色 grandfather->_col = RED; } else //双旋 { rotateL(parent); //先对p左旋 rotateR(grandfather);//再对g右旋 //变色 cur->_col = BLACK; grandfather->_col = RED; } break; } } else //u在左 { Node* uncle = grandfather->_left; if (uncle && uncle->_col == RED) //u存在且为红 { parent->_col = BLACK; //p和u变黑 uncle->_col = BLACK; grandfather->_col = RED;//g变红 cur = grandfather; //继续向上更新 parent = cur->_parent; } else //u不存在 或 存在且为黑 { if (cur == parent->_right) //单旋 { rotateL(grandfather);//以g为中心左旋 parent->_col = BLACK; //p变黑 grandfather->_col = RED;//g变红 } else //双旋 { rotateR(parent);//先以p为中心右旋 rotateL(grandfather);//再以g为中心左旋 cur->_col = BLACK; //c变黑 grandfather->_col = RED;//g变红 } break; } } } _root->_col = BLACK; return make_pair(Iterator(newnode, _root), true); }

上面的代码相比之前实现的insert,只有插入部分的代码修改了,旋转部分没做修改。

然后我们用set的insert测试一下。

Myset.h中加上insert的函数,在set类public实现。

代码语言:javascript

AI代码解释

public: pair<iterator, bool> insert(const K& key) //插入 { return _t.insert(key); } private: RBTree<K, K, Set_Key_Of_T> _t;

Mymap.h中加上insert的函数,在map类public实现。

代码语言:javascript

AI代码解释

public: pair<iterator, bool> insert(const pair<K, V>& kv) //插入 { return _t.insert(kv); } private: RBTree<K, pair<K, V>, Map_Key_Of_T> _t;

test.cpp中测试。如果没报错,目前这个insert就是对的。

代码语言:javascript

AI代码解释

#include "Myset.h" #include "Mymap.h" int main() { lyj::set<int> s; //用自己实现的set s.insert(5); s.insert(3); s.insert(2); s.insert(4); s.insert(1); return 0; }

2. iterator 迭代器的实现

2.1 部分运算符重载

这里需要同时考虑普通的迭代器和const迭代器,所以还是要一个类模板来实现。

代码语言:javascript

AI代码解释

template<class T, class ref, class ptr> struct RBTree_Iterator { typedef RBTreeNode<T> Node; typedef RBTree_Iterator<T, ref, ptr> Self; };

还需要一个节点的指针,并写一个构造函数。

代码语言:javascript

AI代码解释

template<class T, class ref, class ptr> struct RBTree_Iterator { typedef RBTreeNode<T> Node; typedef RBTree_Iterator<T, ref, ptr> Self; Node* _node; RBTree_Iterator(Node* node) :_node(node) {} };

然后还是在类里实现一下operator*, operator->, operator!= 和 operator== 这4个运算符重载。

代码语言:javascript

AI代码解释

ref operator*() { return _node->_data; } ptr operator->() { return &_node->_data; } bool operator!=(const Self& s) const { return _node != s._node; } bool operator==(const Self& s) const { return _node == s._node; }

然后在RBTree类里在重命名一下。

代码语言:javascript

AI代码解释

typedef typename RBTree_Iterator<T, T&, T*> Iterator; typedef typename RBTree_Iterator<T, const T&, const T*> const_Iterator;

2.2 迭代器
2.2.1 begin和end

以下图为例,map和set的迭代器⾛的是中序遍历,左⼦树->根结点->右⼦树,那么begin()会返回中序第⼀个结点的iterator也就是10 所在结点的迭代器。

end()如何表⽰呢?图中,当it指向50时,++it时,50是40的右,40是30的右,30是18的右,18

到根没有⽗亲,没有找到孩⼦是⽗亲左的那个祖先,这是⽗亲为空了,那我们就把it中的结点指针

置为nullptr,我们⽤nullptr去充当end

RBTree.hRBTree类中public实现。

代码语言:javascript

AI代码解释

Iterator Begin() { Node* cur = _root; while (cur && cur->_left) { cur = cur->_left; //找最左节点 } return Iterator(cur); } const_Iterator Begin() const //const迭代器 { Node* cur = _root; while (cur && cur->_left) { cur = cur->_left; //找最左节点 } return Iterator(cur); }

代码语言:javascript

AI代码解释

Iterator End() { return Iterator(nullptr); } const_Iterator End() const //const迭代器 { return Iterator(nullptr); }

Myset.hset类里要封装一下。

代码语言:javascript

AI代码解释

public: typedef typename RBTree<K, K, Set_Key_Of_T>::Iterator iterator; typedef typename RBTree<K, K, Set_Key_Of_T>::const_Iterator const_iterator; iterator begin() { return _t.Begin(); } iterator end() { return _t.End(); } const_iterator begin() const //const迭代器 { return _t.Begin(); } const_iterator end() const { return _t.End(); }

Mymap.hmap类里也要封装一下。

代码语言:javascript

AI代码解释

public: typedef typename RBTree<K, pair<K, V>, Map_Key_Of_T>::Iterator iterator; typedef typename RBTree<K, pair<K, V>, Map_Key_Of_T>::const_Iterator const_iterator; iterator begin() { return _t.Begin(); } iterator end() { return _t.End(); } const_iterator begin() const //const迭代器 { return _t.Begin(); } const_iterator end() const { return _t.End(); }
2.2.2 ++和--

迭代器++的核⼼逻辑就是不看全局,只看局部,只考虑当前中序局部要访问的下⼀个结点。

  • 迭代器++时,如果it指向的结点的右⼦树不为空,代表当前结点已经访问完了,要访问下⼀个结点是右⼦树的中序第⼀个,⼀棵树中序第⼀个是最左结点,所以直接找右⼦树的最左结点即可。
  • 迭代器++时,如果it指向的结点的右⼦树空,代表当前结点已经访问完了且当前结点所在的⼦树也访问完了,要访问的下⼀个结点在当前结点的祖先⾥⾯,所以要沿着当前结点到根的祖先路径向上找。

如果当前结点是⽗亲的左,根据中序左⼦树->根结点->右⼦树,那么下⼀个访问的结点就是当前结点的⽗亲。

如下图:it指向25,25右为空,25是30的左,所以下⼀个访问的结点就是30。

www.dongchedi.com/article/7598662619591098905
www.dongchedi.com/article/7598661663487148606
www.dongchedi.com/article/7598661165073383960
www.dongchedi.com/article/7598661590610706968
www.dongchedi.com/article/7598659598723203609
www.dongchedi.com/article/7598660331644518974
www.dongchedi.com/article/7598659182870970942
www.dongchedi.com/article/7598659511842849304
www.dongchedi.com/article/7598658577318265369
www.dongchedi.com/article/7598660363760206398
www.dongchedi.com/article/7598659848598962750
www.dongchedi.com/article/7598658231610884632
www.dongchedi.com/article/7598655027276071449
www.dongchedi.com/article/7598656521844949566
www.dongchedi.com/article/7598656663327261246
www.dongchedi.com/article/7598657570517058110
www.dongchedi.com/article/7598656249982550590
www.dongchedi.com/article/7598655008087196222
www.dongchedi.com/article/7598654082278408728
www.dongchedi.com/article/7598654850569667096
www.dongchedi.com/article/7598653079990911513
www.dongchedi.com/article/7598653503171052094
www.dongchedi.com/article/7598653873355948569
www.dongchedi.com/article/7598651016690975257
www.dongchedi.com/article/7598653584766632510
www.dongchedi.com/article/7598650859257397785
www.dongchedi.com/article/7598650344322925081

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

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

相关文章

Cursor功能优化指南:理解限制机制与合规使用方案

Cursor功能优化指南&#xff1a;理解限制机制与合规使用方案 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your trial re…

模型微调前准备:DeepSeek-R1作为基座模型的适配性分析

模型微调前准备&#xff1a;DeepSeek-R1作为基座模型的适配性分析 在开始微调一个大语言模型之前&#xff0c;很多人会直接跳到“怎么改参数”“怎么写LoRA配置”&#xff0c;却忽略了最关键的第一步&#xff1a;这个模型本身&#xff0c;真的适合你的任务吗&#xff1f;它是不…

如何清除重新来?fft npainting lama重置按钮使用方法

如何清除重新来&#xff1f;FFT NPainting Lama重置按钮使用方法 1. 为什么需要“清除”功能&#xff1f; 在图像修复过程中&#xff0c;你可能遇到这些情况&#xff1a; 标注区域画错了&#xff0c;想从头开始上传了错误的图片&#xff0c;想换一张重新操作修复效果不理想&…

智谱开源Glyph体验分享:长文本变图像处理新思路

智谱开源Glyph体验分享&#xff1a;长文本变图像处理新思路 你有没有试过让大模型读完一篇3000字的产品说明书&#xff0c;再让它精准生成一张带完整文案的电商海报&#xff1f;传统方法要么卡在上下文长度限制里&#xff0c;要么文字糊成一团、错字连篇——直到我遇见Glyph。…

YOLO11参数详解:train.py关键配置解读

YOLO11参数详解&#xff1a;train.py关键配置解读 YOLO11并不是当前主流开源社区中真实存在的官方模型版本。截至2024年&#xff0c;Ultralytics官方发布的最新稳定版为YOLOv8&#xff0c;后续迭代包括实验性分支YOLOv9、YOLOv10&#xff08;由其他研究团队提出&#xff09;&a…

Llama3-8B省钱部署方案:单卡3060实现高性能推理案例

Llama3-8B省钱部署方案&#xff1a;单卡3060实现高性能推理案例 1. 为什么说Llama3-8B是“性价比之王” 你是不是也遇到过这样的困扰&#xff1a;想跑一个真正好用的大模型&#xff0c;但显卡预算只有几千块&#xff1f;RTX 4090太贵&#xff0c;A100租不起&#xff0c;连309…

工业自动化中RS485和RS232通信协议选型指南:全面讲解

以下是对您提供的博文《工业自动化中RS485和RS232通信协议选型指南:全面技术解析》的 深度润色与结构化重写版本 。本次优化严格遵循您的全部要求: ✅ 彻底去除AI痕迹,语言更贴近一线工程师口吻与教学博主风格; ✅ 打破模板化标题(如“引言”“总结”),全文以自然逻…

汽车电子中I2C中断TC3配置:系统学习与实践指南

以下是对您原始博文的 深度润色与工程化重构版本 。全文已彻底去除AI生成痕迹&#xff0c;采用真实嵌入式工程师口吻写作&#xff1a;有经验判断、有踩坑总结、有设计权衡、有代码细节、有调试直觉——不再是“教科书式罗列”&#xff0c;而是 一位在TC3项目中调通过EEPROM校…

如何突破Cursor功能限制:专业级解决方案全解析

如何突破Cursor功能限制&#xff1a;专业级解决方案全解析 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your trial requ…

实测对比:传统方法 vs fft npainting lama修复效果差异

实测对比&#xff1a;传统方法 vs FFT LaMa修复效果差异 图像修复这件事&#xff0c;说简单也简单——把照片里不想看到的东西抹掉&#xff1b;说难也难——抹得自然、不露痕迹、颜色协调、纹理连贯&#xff0c;才是真功夫。市面上的修复工具不少&#xff0c;从Photoshop的“内…

YOLO26低成本部署方案:中小企业也能轻松上手的实战指南

YOLO26低成本部署方案&#xff1a;中小企业也能轻松上手的实战指南 你是不是也遇到过这样的问题&#xff1a;想用最新的YOLO26做目标检测&#xff0c;但一打开官方文档就看到密密麻麻的依赖安装、CUDA版本匹配、环境冲突报错……最后只能放弃&#xff1f;更别说还要自己配训练…

NewBie-image-Exp0.1影视预研案例:角色概念图自动化生成实战

NewBie-image-Exp0.1影视预研案例&#xff1a;角色概念图自动化生成实战 1. 为什么影视预研需要角色概念图自动化&#xff1f; 在动画、游戏、短剧等视觉内容的前期开发中&#xff0c;角色概念图是决定项目调性与制作方向的关键一环。传统流程依赖原画师手绘草稿、反复修改、…

STM32低功耗应用中I2C读写EEPROM代码优化技巧

以下是对您提供的技术博文进行 深度润色与结构重构后的专业级技术文章 。全文已彻底去除AI生成痕迹&#xff0c;采用资深嵌入式工程师第一人称视角撰写&#xff0c;语言自然、逻辑严密、节奏紧凑&#xff0c;兼具教学性、实战性与思想深度。所有技术细节均严格基于STM32官方参…

Qwen3-0.6B API调用超时?网络配置优化实战指南

Qwen3-0.6B API调用超时&#xff1f;网络配置优化实战指南 1. 为什么Qwen3-0.6B会频繁超时&#xff1f; 你刚部署好Qwen3-0.6B镜像&#xff0c;打开Jupyter Notebook&#xff0c;复制粘贴那段LangChain调用代码&#xff0c;满怀期待地敲下chat_model.invoke("你是谁&…

ESP32教程:使用Arduino IDE实现蓝牙通信实战案例

以下是对您提供的博文内容进行 深度润色与结构重构后的专业级技术文章 。全文已彻底去除AI生成痕迹&#xff0c;强化了工程师视角的实战逻辑、教学节奏与经验沉淀&#xff1b;摒弃模板化标题与刻板段落&#xff0c;代之以自然递进、层层深入的技术叙事&#xff1b;所有代码、…

低成本高效率:自建AI手机助理详细教程

低成本高效率&#xff1a;自建AI手机助理详细教程 摘要&#xff1a;本文手把手教你用一台普通电脑一部安卓手机&#xff0c;零成本搭建专属AI手机助理。无需云服务、不上传截图、不依赖API收费&#xff0c;所有计算在本地完成。从环境配置到真机操控&#xff0c;从基础指令到复…

2026年AI图像生成入门必看:Qwen开源模型+ComfyUI镜像实战

2026年AI图像生成入门必看&#xff1a;Qwen开源模型ComfyUI镜像实战 你是不是也试过在本地跑图像生成模型&#xff0c;结果卡在环境配置、依赖冲突、CUDA版本不匹配上&#xff1f;折腾三天&#xff0c;连第一张图都没出来&#xff1f;别急——这次我们不讲原理、不堆参数、不聊…

CAPL脚本中定时器在CAN测试中的使用:全面讲解

以下是对您提供的博文内容进行深度润色与结构重构后的技术文章。我以一位深耕汽车电子测试多年、兼具Vector工具链实战经验与AUTOSAR/UDS协议栈理解的一线测试架构师视角&#xff0c;对原文进行了全面重写&#xff1a;✅彻底去除AI腔调与模板化表达&#xff08;如“本文将从………

Sambert开发避坑指南:常见报错及解决方案汇总

Sambert开发避坑指南&#xff1a;常见报错及解决方案汇总 1. 镜像核心能力与适用场景 Sambert 多情感中文语音合成-开箱即用版&#xff0c;专为快速落地语音合成需求设计。它不是需要反复编译、调试依赖的“半成品”&#xff0c;而是经过深度打磨的生产就绪型镜像——你拉取即…

GLM-Edge-V-5B:5B轻量模型让边缘设备秒懂图文!

GLM-Edge-V-5B&#xff1a;5B轻量模型让边缘设备秒懂图文&#xff01; 【免费下载链接】glm-edge-v-5b 项目地址: https://ai.gitcode.com/zai-org/glm-edge-v-5b 导语&#xff1a;近日&#xff0c;一款名为GLM-Edge-V-5B的轻量级多模态模型正式亮相&#xff0c;其50亿…