深入剖析:基于红黑树实现自定义 map 和 set 容器
🌟 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。🌟
在 C++ 标准模板库(STL)的大家庭里,map
和set
可是超级重要的关联容器成员呢😎!它们凭借红黑树这一强大的数据结构,实现了查找、插入和删除等操作的高效运行。
现在,就让我们一步步深入探索如何亲手基于红黑树打造自定义的map
和set
容器吧🧐!
目录
深入剖析:基于红黑树实现自定义 map 和 set 容器 🚀
一、红黑树基础结构与操作 🌳
1. 红黑树节点结构 📄
2. 红黑树类基本框架 📐
3. 左旋操作 🔄
4. 右旋操作 🔄
5. 插入修复 🔧
6. 插入操作 📥
7. 查找操作 🔍
8. 红黑树析构函数 🗑️
二、自定义 map 和 set 实现 🛠️
1. 自定义 set 实现 📚
(1)SetKeyOfT 仿函数 📐
2. 自定义 map 实现 🗺️
(1)MapKeyOfT 仿函数 📐
三、测试代码 🧪
一、红黑树基础结构与操作 🌳
1. 红黑树节点结构 📄
红黑树的节点结构包含节点颜色、父节点指针、左右子节点指针以及存储的数据。
- 实现思路:定义一个结构体来表示红黑树的节点,包含节点颜色、父节点指针、左右子节点指针和存储的数据。为方便后续操作,节点默认颜色设为红色,新插入节点通常为红色,有助于维持红黑树性质。
- 代码实现:
// 定义红黑树节点颜色
enum Colour {RED,BLACK
};// 红黑树节点结构体
template<class T>
class RBTreeNode {
public:RBTreeNode(const T& data) : _data(data),_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED){}RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;T _data;
};
2. 红黑树类基本框架 📐
- 实现思路:定义红黑树类,包含根节点指针。提供插入、查找等基本操作函数声明,用于数据的插入、检索;同时定义左旋、右旋等内部操作函数声明,用于调整树结构,维持红黑树性质。
- 代码实现:
template<class K, class T, class KeyOfT>
class RBTree {
public:typedef RBTreeNode<T> Node;typedef _TreeIterator<T, T&, T*> iterator;typedef _TreeIterator<T, const T&, const T*> const_iterator;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;pair<Node*, bool> insert(const T& data);private:Node* _root = nullptr;void RotateL(Node* parent);void RotateR(Node* parent);void RotateRL(Node* parent);void RotateLR(Node* parent);
};
3. 左旋操作 🔄
实现步骤👇
-
保存关键节点指针:
- 保存
parent
的右子节点subR
和subR
的左子节点subRL
。
- 保存
-
调整
parent
的右子节点:- 将
parent
的右子节点更新为subRL
。 - 如果
subRL
不为空,将subRL
的父节点指针指向parent
。
- 将
-
调整
subR
的左子节点:- 将
subR
的左子节点更新为parent
。 - 保存
parent
的父节点parentParent
。 - 将
parent
的父节点指针指向subR
。
- 将
-
更新根节点或父节点的子节点指针:
- 如果
parent
是根节点(即parentParent
为空),将subR
设为新的根节点,并将subR
的父节点指针置为空。 - 否则,根据
parent
是parentParent
的左子节点还是右子节点,更新parentParent
的相应子节点指针为subR
,并将subR
的父节点指针指向parentParent
。
- 如果
代码实现:
template<class K, class T, class KeyOfT>
void RBTree<K, T, KeyOfT>::RotateL(Node* parent) { Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) {subRL->_parent = parent;}subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (parentParent == nullptr) {_root = subR;subR->_parent = nullptr;}else {if (parentParent->_left == parent) {parentParent->_left = subR;}else {parentParent->_right = subR;}subR->_parent = parentParent;}
}
4. 右旋操作 🔄
- 实现思路:右旋操作以
parent
为中心,交换其与左子节点subL
位置,调整subLR
指针,更新根或父节点指向维持平衡 - 代码实现:
template<class K, class T, class KeyOfT>
void RBTree<K, T, KeyOfT>::RotateR(Node* parent) { Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subL->_right;if (subLR) {subLR->_parent = parent;}subL->_right = parent;Node* parentParent = parent->_parent;parent->_parent = subL;if (parentParent == nullptr) {_root = subL;subL->_parent = nullptr;}else {if (parentParent->_left == parent) {parentParent->_left = subL;}else {parentParent->_right = subL;}subL->_parent = parentParent;}
}
5. 插入修复 🔧
- 实现思路:插入新节点后,红黑树的性质可能会被破坏,需要进行修复操作。修复操作主要根据新节点的父节点和叔节点的颜色进行分类处理,通过旋转和颜色调整来恢复红黑树的性质。
- 代码实现:
pair<Node*,bool> insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* cur = _root;Node* parent = nullptr;KeyOfT kot;while (cur){parent = cur;if (kot(cur->_data) < kot(data)){cur = cur->_right;}else if (kot(cur->_data) > kot(data)){cur = cur->_left;}else{return make_pair(cur, false);}}//新增结点给红色cur = new Node(data);Node* newNode = cur;cur->_parent = parent;if (kot(parent->_data) < kot(cur->_data)){parent->_right = cur;}else{parent->_left = cur;}while (parent && parent->_col == RED){//找叔叔Node* grandfather = parent->_parent;Node* uncle = nullptr;if (parent == grandfather->_left){uncle = grandfather->_right;}else{uncle = grandfather->_left;}if (uncle && uncle->_col == RED)//满足规则一,父亲和叔叔变黑,爷爷变红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上更新cur = grandfather;parent = grandfather->_parent;}else //叔叔 不存在 或 存在且为黑{if (parent == grandfather->_left && cur == parent->_left)//右单旋{RotateR(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && cur == parent->_right)//左单旋{RotateL(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_left && cur == parent->_right)//折射,双旋{RotateLR(grandfather);//变色cur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && cur == parent->_left)//折射,双旋{RotateRL(grandfather);//变色cur->_col = BLACK;grandfather->_col = RED;}}}_root->_col = BLACK;return make_pair(newNode, true);
}
6. 插入操作 📥
- 实现思路:首先找到合适的插入位置,创建新节点并插入到树中,然后调用插入修复函数来恢复红黑树的性质。
- 代码实现:
template<class K, class T, class KeyOfT>
pair<typename RBTree<K, T, KeyOfT>::Node*, bool> RBTree<K, T, KeyOfT>::insert(const T& data) {if (_root == nullptr) {_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* cur = _root;Node* parent = nullptr;KeyOfT kot;while (cur) {parent = cur;if (kot(cur->_data) < kot(data)) {cur = cur->_right;}else if (kot(cur->_data) > kot(data)) {cur = cur->_left;}else {return make_pair(cur, false);}}// 插入新节点并设置颜色cur = new Node(data);Node* newNode = cur;cur->_parent = parent;if (kot(parent->_data) < kot(cur->_data)) {parent->_right = cur;}else {parent->_left = cur;}while (parent && parent->_col == RED) {// 情况分类Node* grandfather = parent->_parent;Node* uncle = nullptr;if (parent == grandfather->_left) {uncle = grandfather->_right;}else {uncle = grandfather->_left;}if (uncle && uncle->_col == RED) { // 叔叔节点存在且为红色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上调整cur = grandfather;parent = grandfather->_parent;}else { // 叔叔节点不存在或为黑色if (parent == grandfather->_left && cur == parent->_left) { // 左左情况RotateR(grandfather);// 调整颜色parent->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && cur == parent->_right) { // 右右情况RotateL(grandfather);// 调整颜色parent->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_left && cur == parent->_right) { // 左右情况RotateLR(grandfather);// 调整颜色cur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && cur == parent->_left) { // 右左情况RotateRL(grandfather);// 调整颜色cur->_col = BLACK;grandfather->_col = RED;}}}_root->_col = BLACK;return make_pair(newNode, true);
}
7. 查找操作 🔍
- 实现思路:从根节点开始,根据键的大小比较,递归地在左子树或右子树中查找目标节点。
- 代码实现:
template<class K, class T, class KeyOfT>
RBTreeNode<T>* RBTree<K, T, KeyOfT>::Find(const K& key) {RBTreeNode<T>* current = _root;KeyOfT kot;while (current) {if (key < kot(current->_data)) {current = current->_left;}else if (key > kot(current->_data)) {current = current->_right;}else {return current;}}return nullptr;
}
8. 红黑树析构函数 🗑️
- 实现思路:采用后序遍历的方式递归删除树中的所有节点,释放内存。
- 代码实现:
template<class K, class T, class KeyOfT>
RBTree<K, T, KeyOfT>::~RBTree() {auto destroyTree = [](RBTreeNode<T>* node) {if (node != nullptr) {destroyTree(node->left);destroyTree(node->right);delete node;}};destroyTree(_root);
}
二、自定义 map 和 set 实现 🛠️
1. 自定义 set 实现 📚
(1)SetKeyOfT 仿函数 📐
- 实现思路:由于
set
中存储的元素就是键,因此仿函数直接返回元素本身。 - 代码实现:
namespace zdf {template<class K>class set {public:struct SetKeyOfT {const K& operator()(const K& key) {return key;}};// 定义迭代器类型typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin() const {return _t.begin();}iterator end() const {return _t.end();}pair<iterator, bool> insert(const K& key) {return _t.insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}
2. 自定义 map 实现 🗺️
(1)MapKeyOfT 仿函数 📐
- 实现思路:
map
中存储的是键值对,仿函数从键值对中提取键。 - 代码实现:
namespace zdf {template<class K, class V>class map {struct MapKeyOfT {const K& operator()(const std::pair<K, V>& kv) {return kv.first;}};public:typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin() {return _t.begin();}iterator end() {return _t.end();}V& operator[](const K& key) {std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const std::pair<K, V>& kv) {return _t.insert(kv);}private:RBTree<K, std::pair<const K, V>, MapKeyOfT> _t;};
}
三、测试代码 🧪
#include <iostream>int main() {// 测试setzdf::set<int> s;s.insert(3);s.insert(1);s.insert(2);// 测试mapzdf::map<int, std::string> m;m.insert({ 1, "one" });m.insert({ 2, "two" });m[3] = "three";std::cout << "Map value at key 3: " << m[3] << std::endl;return 0;
}
通过以上步骤,我们基于红黑树实现了自定义的map
和set
容器,每个函数的实现思路和代码都进行了详细讲解。这些实现展示了红黑树在关联容器中的重要应用,以及如何通过封装和扩展红黑树来实现高效的数据存储和操作。 🎉
欢迎关注我 👉【A charmer】
相关文章:

深入剖析:基于红黑树实现自定义 map 和 set 容器
🌟 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。🌟 在 C 标准模板库(STL)的大家庭里,map和set可是超级重要的关联容器成员呢😎&#x…...
在大数据项目中如何设计和优化数据模型
在大数据项目中,设计和优化数据模型是一个涉及多个步骤和维度的复杂过程。以下是我通常采取的方法: 一、数据模型设计 明确业务需求: 深入了解项目的业务场景和目标,明确数据模型需要解决的具体问题。与业务团队紧密合作…...
JavaScript querySelector()、querySelectorAll() CSS选择器解析(DOM元素选择)
文章目录 基于querySelector系列方法的CSS选择器深度解析一、方法概述二、基础选择器类型1. 类型选择器2. ID选择器3. 类选择器4. 属性选择器 三、组合选择器1. 后代组合器2. 子元素组合器3. 相邻兄弟组合器4. 通用兄弟组合器 四、伪类与伪元素1. 结构伪类2. 状态伪类3. 内容伪…...
Linux系统中处理子进程的终止问题
1. 理解子进程终止的机制 在Unix/Linux系统中,当子进程终止时,会向父进程发送一个SIGCHLD信号。父进程需要捕捉这个信号,并通过调用wait()或waitpid()等函数来回收子进程的资源。这一过程被称为“回收僵尸进程”。 如果父进程没有及时调用w…...
Docker 不再难懂:快速掌握容器命令与架构原理
1. Docker 是容器技术的一种 容器(Container)概述 容器(Container)是一种轻量级的虚拟化技术,它将应用程序及其所有依赖环境打包在一个独立的、可移植的运行时环境中。容器通过操作系统级的虚拟化提供隔离࿰…...
取消票证会把指定的票证从数据库中删除,同时也会把票证和航班 等相关表中的关联关系一起删除。但在删除之前,它会先检查当前用户是否拥有这张票
在做航班智能客服问答系统时会遇到取消票证的场景,这里涉及数据库的操作时会把指定的票证从数据库中删除,同时也会把票证和航班等相关表中的关联关系一起删除。但在删除之前,需要先检查当前用户是否拥有这张票,只有票主才有权限取…...
力扣-贪心-763 划分字母区间
思路 先统计字符串中每一个字母出现的最后下标,然后从end初始化为第一个字母出现的最后下标,在i<end时,不断更新end,因为一旦囊括新的字母就最起码要遍历到新字母出现的最后下标,在i>end时,说明遍历…...

【Redis 原理】网络模型
文章目录 用户空间 && 内核空间阻塞IO非阻塞IO信号驱动IO异步IOIO多路复用selectpollepoll Web服务流程Redis 网络模型Redis单线程网络模型的整个流程Redis多线程网络模型的整个流程 用户空间 && 内核空间 为了避免用户应用导致冲突甚至内核崩溃,用…...

cpp中的继承
一、继承概念 在cpp中,封装、继承、多态是面向对象的三大特性。这里的继承就是允许已经存在的类(也就是基类)的基础上创建新类(派生类或者子类),从而实现代码的复用。 如上图所示,Person是基类&…...
DeepSeek全栈接入指南:从零到生产环境的深度实践
第一章:DeepSeek技术体系全景解析 1.1 认知DeepSeek技术生态 DeepSeek作为新一代人工智能技术平台,构建了覆盖算法开发、模型训练、服务部署的全链路技术栈。其核心能力体现在: 1.1.1 多模态智能引擎 自然语言处理:支持文本生成(NLG)、语义理解(NLU)、情感分析等计算…...
CSS 真的会阻塞文档解析吗?
在网页开发领域,一个常见的疑问是 CSS 是否会阻塞文档解析。理解这一问题对于优化网页性能、提升用户体验至关重要。要深入解答这个问题,需要从浏览器渲染网页的原理说起。 浏览器渲染网页的基本流程 浏览器在接收到 HTML 文档后,会依次进行…...

大模型的UI自动化:Cline 使用Playwright MCP Server完成测试
大模型的UI自动化:Cline 使用Playwright MCP Server完成测试 MCP MCP(Model Context Protocol),是一个开发的协议,标准化了应用程序如何为大模型提供上下文。MCP提供了一个标准的为LLM提供数据、工具的方式,使用MCP会更容易的构建Agent或者是基于LLM的复杂工作流。 最近…...

碰撞检测 | 图解凸多边形分离轴定理(附ROS C++可视化)
目录 0 专栏介绍1 凸多边形碰撞检测2 多边形判凸算法3 分离轴定理(SAT)4 算法仿真与可视化4.1 核心算法4.2 仿真实验 0 专栏介绍 🔥课设、毕设、创新竞赛必备!🔥本专栏涉及更高阶的运动规划算法轨迹优化实战,包括:曲线…...
Python 基本数据类型
目录 1. 字符串(String) 2. 列表(List) 3. 字典(Dictionary) 4. 集合(Set) 5. 数字(Number) 6. 布尔值(Boolean) 1. 字符串&…...

突破“第一崇拜“:五维心理重构之路
一、视频介绍 在这个崇尚"第一"的时代,我们如何找到自己的独特价值?本视频将带您踏上五维心理重构之旅,从诗意人生的角度探讨如何突破"圣人之下皆蝼蚁"的局限。我们将穿越人生的不同阶段,从青春的意气风发到…...

KubeKey一键安装部署k8s集群和KubeSphere详细教程
目录 一、KubeKey简介 二、k8s集群KubeSphere安装 集群规划 硬件要求 Kubernetes支持版本 操作系统要求 SSH免密登录 配置集群时钟 所有节点安装依赖 安装docker DNS要求 存储要求 下载 KubeKey 验证KubeKey 配置集群文件 安装集群 验证命令 登录页面 一、Ku…...
UE5网络通信架构解析
文章目录 前言一、客户端-服务器架构(C/S Model)二、对等网络架构(P2P,非原生支持)三、混合架构(自定义扩展)四、UE5网络核心机制 前言 UE5的网络通信主要基于客户端-服务器(C/S&am…...

实验3 知识表示与推理
实验3 知识表示与推理 一、实验目的 (1)掌握知识和知识表示的基本概念,理解其在AI中的深刻含义与意义; (2)熟悉AI中常用的知识表示方法的优缺点及其应用场景; (3)掌握产…...

基于Springboot银行信用卡额度管理系统【附源码】
基于Springboot银行信用卡额度管理系统 效果如下: 系统登陆页面 用户个人中心页面 新增信用卡申请页面 评估审核页面 管理员主页面 评估审核页面 操作日志管理页面 消费页面 研究背景 随着金融行业的快速发展和信息技术的不断进步,信用卡作为一种便捷…...
达梦数据库学习笔记@1
目录 达梦数据库学习笔记一、表空间管理(一)默认表空间(二)相关数据字典(三)表空间操作(四)临时表空间管理 二、重做日志管理(一)系统视图(二&…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...