详细介绍:扒透 STL 底层!map/set 如何封装红黑树?迭代器逻辑 + 键值限制全手撕----《Hello C++ Wrold!》(23)--(C/C++)

news/2025/9/30 10:16:53/文章来源:https://www.cnblogs.com/ljbguanli/p/19120078

详细介绍:扒透 STL 底层!map/set 如何封装红黑树?迭代器逻辑 + 键值限制全手撕----《Hello C++ Wrold!》(23)--(C/C++)

文章目录

  • 前言
  • map和set的封装
  • 底层红黑树的模拟实现
  • 迭代器的模拟实现

前言

你是不是也有过这种 “知其然不知其所以然” 的困惑:
用 map 存键值对、用 set 去重排序时很顺手,但一被问 “map 的 [] 怎么既插入又访问”“set 为啥不能改元素”“它们底层的红黑树到底存的啥”,就瞬间卡壳?甚至看 STL 源码时,被 “KeyOfT”“迭代器 ++ 逻辑” 绕得晕头转向?

其实 map 和 set 的本质,就是对红黑树的 “定制化封装” —— 红黑树是 “通用骨架”,map 和 set 通过 “提取键的规则(KeyOfT)”“迭代器权限控制”“键值修改限制”,分别适配了 “键值对存储” 和 “单一元素去重” 的需求。

这篇内容不搞虚的,直接从 “红黑树如何适配 map/set” 切入:拆透 map 的 KeyOfT 怎么提取 pair 的 first,set 为啥要用 const 迭代器锁死修改;讲清迭代器 +±- 的底层逻辑(右子树找最左 vs 回溯父节点);还附上手撕的红黑树封装、迭代器实现代码 —— 不管你是想搞懂 STL 底层,还是面试被问 “map/set 原理” 时想答到点子上,这篇都能让你把 “封装逻辑” 嚼得明明白白。

map和set的封装

这俩在封装的时候对红黑树里面的K,T的处理:

map的话K是存Key,T是搞的pair<Key,Value> --这两个Key是一样的哈

set的话K是存Key,T也是存Key–这两个Key是一样的哈(第二个T存东西单纯为了陪跑)

在这里插入图片描述

对于set的话KT都不能被修改–因为都是Key

对于map的话K不能被修改,T里面的value可以被修改

对于键和值的理解:

对于map:键用来排序查找啥的,值用来存信息

对于set:键承担了所有

map:
namespace renshen
{
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;//这里必须加typename,不然编译器不指定RBTree<K, pair<K, V>, MapKeyOfT>::iterator是个类型//因为,类模板只有在被实例化的时候,编译器才会真正知道其中各种类型和表达式的含义//之后在源文件的话就可以eg:renshen::map<int,int>::iterator mit = ......//如果iterator被typedef过的话,RBTree<K, pair<const K, V>, MapKeyOfT>::iterator还是RBTree里面的iteratortypedef 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();}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}//V()就是为了没有这个key的话就插入,有的话就返回已有的那个值pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;//这个const用的好,让普通迭代器的value可以修改,const迭代器啥也不能修改};}

map[]可以插入和修改和访问–string[]不能用来插入

set:
namespace renshen
{
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;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);return pair<iterator, bool>(ret.first, ret.second);}private:RBTree<K, K, SetKeyOfT> _t;};}

底层红黑树的模拟实现

enum Colour
{
RED,
BLACK
};
template<class T>struct RBTreeNode{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_data(data),_col(RED){}};template<class K, class T, class KeyOfT>struct RBTree{typedef RBTreeNode<T> Node;public:typedef __TreeIterator<T, T*, T&> iterator;typedef __TreeIterator<T, const T*, const T&> const_iterator;iterator begin(){Node* leftMin = _root;while (leftMin && leftMin->_left)//这里前面那个leftMin是防止这个树是空树{leftMin = leftMin->_left;}//最左边的那个节点return iterator(leftMin);//调用了iterator的构造函数}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return const_iterator(leftMin);}const_iterator end() const{return const_iterator(nullptr);}Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return cur;}}return nullptr;}pair<iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < kot(data))//这样搞比较好些{parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(cur), false);//返回已经存在的那个节点的迭代器}}cur = new Node(data);cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;// u存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // u不存在 或 存在且为黑{if (cur == parent->_left){//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   p//		cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // parent == grandfather->_right{Node* uncle = grandfather->_left;// u存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){// g//	  p//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);}//旋转相关的代码就参考AVL树的就行了private:Node* _root = nullptr;};

库里面的红黑树和这里的模拟实现的区别:

库里面其实还有个头节点,头节点的父亲是红黑树的根节点,头节点的left是红黑树里面最小的数,头节点的right是红黑树里面最大的数,然后根节点的父亲也是头节点

注意:区分头节点和根节点!

迭代器的模拟实现

这里的迭代器的beginend的位置要注意:

begin是最左边的那个节点

end是根节点的父亲–nullptr–这个beginend是在底层红黑树那里定义的

这里的迭代器的++:

1.当前节点的右子树不为空,则访问右树的最左节点

2.当前节点的右子树为空,如果当前节点的父亲是其父亲的左孩子,就访问当前节点的父亲,不然就把当前节点的父亲变成当前节点,直到遇到那种情况或者当前节点成根节点的父亲了

这里迭代器的--:

1.当前节点的左子树不为空,则访问左树的最右节点

2.当前节点的右子树为空,如果当前节点的父亲是其父亲的右孩子,就访问当前节点的父亲,不然就把当前节点的父亲变成当前节点,直到遇到那种情况或者当前节点变成根节点的父亲了

引申:普通迭代器可以隐式转换为const迭代器

   const迭代器不能隐式转换成普通迭代器,强转有时可以
template<class T, class Ptr, class Ref>struct __TreeIterator{typedef RBTreeNode<T> Node;typedef __TreeIterator<T, Ptr, Ref> Self;typedef __TreeIterator<T, T*, T&> Iterator;__TreeIterator(const Iterator& it):_node(it._node){}Node* _node;__TreeIterator(Node* node):_node(node){}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;}Self& operator--(){if (_node->_left){Node* subRight = _node->_left;while (subRight->_right){subRight = subRight->_right;}_node = subRight;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator++(){if (_node->_right){// 右树的最左节点(最小节点)Node* subLeft = _node->_right;while (subLeft->_left){subLeft = subLeft->_left;}_node = subLeft;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}};

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

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

相关文章

死锁的处理策略-预防死锁

破坏互斥条件 对于必须互斥使用资源的争抢才会导致死锁 如果吧互斥使用的资源改为允许共享使用,则系统不会进入死锁状态 比如SPOOLing技术: 就以打印机为例子,假如进程1和进程2都申请使用打印机,SPOOLing会在两个进…

跨网文件安全交换系统:提升数据传输安全性和合规性

跨网文件安全交换系统是现代企业管理中不可或缺的一部分,特别是Ftrans Ferry跨网文件安全交换系统和内外网文件摆渡系统,正好契合了对敏感信息保护需求日益增长的趋势。这些系统通过先进的加密技术和严格的身份验证,…

企业网站注册域名的步骤网站建设培训价格

效果: 1.编译FFmpeg库: 下载FFmpeg-kit的源码并编译生成安装平台库 2.复制生成的FFmpeg库so文件与包含目录到自己的Android下 如果没有prebuiltLibs目录,创建一个,然后复制 包含目录只复制arm64-v8a下

swoole怎么做直播网站wordpress文章不显示图片

2345浏览器安装完成之后默认将2345导航设为主页&#xff0c;但是很多用户其实不习惯用2345导航&#xff0c;那2345浏览器怎么换主页呢?今天小编就教一个方法。 2345浏览器换主页教程 1、首先打开2345浏览器&#xff0c;找到在网站页面的“工具”在浏览器的右上角&#xff0c…

ArcGIS 公众号推荐

ArcGIS 公众号推荐Arcigs带你飞 地学大数据 GIS前沿 小猿猴GISer

跨网文件交换系统:数字化时代企业与机构的数据安全传输利器

如今,随着数字化进程全面提速,任何组织的“生命线”都系在数据能否顺畅流动上。为了守住安全底线,网络隔离已成标配——研发网、办公网、生产网被企业层层切分,医院、政府的内网与外网更是泾渭分明。可越是“隔离”…

软件开发定制app网站首页关键如何优化

作者&#xff1a;饶子昊、杨龙 应用复杂度提升&#xff0c;根因定位困难重重 随着软件技术发展迭代&#xff0c;很多企业软件系统也逐步从单体应用向云原生微服务架构演进&#xff0c;一方面让应用实现高并发、易扩展、开发敏捷度高等效果&#xff0c;但另外一方面也让软件应…

期货市场API对接完全指南:实时行情获取与实战应用

期货市场API对接完全指南:实时行情获取与实战应用本文详细介绍了如何通过API接口获取全球期货市场的实时行情数据,包含完整的代码示例、数据处理方法和实战应用场景。一、期货API概述 期货市场是金融市场的重要组成部…

AT_agc037_c [AGC037C] Numbers on a Circle

你倒着考虑,有一个结论是:如果一个 \(b\) 可以操作,它一定会操作到不能操作为止,然后换别的数操作。这样我们可以维护当前 \(b\) 的最大值,每次操作 \(b\) 至少减半,于是时间复杂度做到了 \(O(n \log n \log V)\…

记账本|基于SSM的家庭记账本小程序设计与实现(源码+数据库+文档) - 实践

记账本|基于SSM的家庭记账本小程序设计与实现(源码+数据库+文档) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: …

爱名网做网站教程制作网线

深度睡眠(C3)和深度睡眠(C4)是用于描述移动平台电源管理的术语。电源管理就是通过将 CPU 置于不使用状态时休眠来延长电池续航时间。C3 深度睡眠和 C4 深度睡眠是 ACPI 电源管理状态。更深的睡眠源自 CPU 和芯片组交互的改进&#xff0c;以重定向 snoop 周期。CPU 的深度 C4 状…

网站建设免费空间注册导航公司名称可以和网站域名不同吗

文章目录 环境配置靶场介绍靶场设置 外网渗透信息收集端口扫描目录扫描 漏洞发现与利用获取ssh账号密码&#xff0c;登录centos 提权 内网渗透建立代理内网信息收集smb暴破&#xff0c;获取本地管理员密码 横向移动使用psexec模块上线msf 环境配置 靶场介绍 靶场地址 http:/…

做网站行业零代码开发平台

最近在看《设计模式与游戏完美开发》&#xff0c;文章将记录一些要点和一些设计模式实现 GoF定义的23种设计模式及应用场景 系统设计可以采用的设计模式&#xff1a;单例、状态&#xff08;场景切换&#xff09;、外观&#xff08;保证高内聚&#xff09;、中介者&#xff08…

缩放 div

缩放 div<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0&q…

Redis从零讲解 - 详解

Redis从零讲解 - 详解2025-09-30 09:50 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-…

如何建设社区网站野花韩国视频在线观看免费高清

知识图谱&#xff1a;浙江大学教授 陈华军 知识图谱 1课时 http://openkg.cn/datasets-type/ 知识图谱的价值 知识图谱是有什么用&#xff1f; 语义搜索 问答系统 QA问答对知识图谱&#xff1a;结构化图 辅助推荐系统 大数据分析系统 自然语言理解 辅助视觉理解 例…

房地产行业网站开发网页界面设计公司

文章目录一、环境分布二、实战1. kafka下载2. 解压3. 配置4. 编写启动脚本5. 编写关闭脚本6. 赋予脚本可执行权限7. 脚本使用案例一、环境分布 软件版本jdk1.8kafkakafka_2.13-2.5.0 二、实战 kafka官网地址&#xff1a; http://kafka.apache.org/downloads 1. kafka下载 …

【2025-09-29】团队合作

20:00没有了家庭,在广大的宇宙间,人会冷得发抖。——安德烈莫罗阿今早比平时提前了15分钟出门送大宝上学。因为公司内部有个产品宣讲会议,时间定在9点半,刚好是我平时上班回到公司的时间。考虑到参会人员都是公司领…

淘宝客api网站架设教程建设公司门户网站

文章目录 一、单选题1、三次握手、四次挥手发生在网络模型的哪一层上&#xff1f;2、互联网Internet的拓扑结构是什么&#xff1f;3、以下哪一种网络设备是工作在网络层的&#xff1f;4、以下哪种关于分组交换网络的说法是错误的&#xff1f;5、以下哪种协议是在TCP/IP模型中的…