当前位置: 首页 > news >正文

深入剖析:基于红黑树实现自定义 map 和 set 容器

🌟 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。🌟 


        在 C++ 标准模板库(STL)的大家庭里,mapset可是超级重要的关联容器成员呢😎!它们凭借红黑树这一强大的数据结构,实现了查找、插入和删除等操作的高效运行

        现在,就让我们一步步深入探索如何亲手基于红黑树打造自定义的mapset容器吧🧐!


目录

深入剖析:基于红黑树实现自定义 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;
}

        通过以上步骤,我们基于红黑树实现了自定义的mapset容器,每个函数的实现思路和代码都进行了详细讲解。这些实现展示了红黑树在关联容器中的重要应用,以及如何通过封装和扩展红黑树来实现高效的数据存储和操作。 🎉

欢迎关注我 👉【A charmer】

 

相关文章:

深入剖析:基于红黑树实现自定义 map 和 set 容器

&#x1f31f; 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。&#x1f31f; 在 C 标准模板库&#xff08;STL&#xff09;的大家庭里&#xff0c;map和set可是超级重要的关联容器成员呢&#x1f60e;&#x…...

在大数据项目中如何设计和优化数据模型

在大数据项目中&#xff0c;设计和优化数据模型是一个涉及多个步骤和维度的复杂过程。以下是我通常采取的方法&#xff1a; 一、数据模型设计 明确业务需求&#xff1a; 深入了解项目的业务场景和目标&#xff0c;明确数据模型需要解决的具体问题。与业务团队紧密合作&#xf…...

JavaScript querySelector()、querySelectorAll() CSS选择器解析(DOM元素选择)

文章目录 基于querySelector系列方法的CSS选择器深度解析一、方法概述二、基础选择器类型1. 类型选择器2. ID选择器3. 类选择器4. 属性选择器 三、组合选择器1. 后代组合器2. 子元素组合器3. 相邻兄弟组合器4. 通用兄弟组合器 四、伪类与伪元素1. 结构伪类2. 状态伪类3. 内容伪…...

Linux系统中处理子进程的终止问题

1. 理解子进程终止的机制 在Unix/Linux系统中&#xff0c;当子进程终止时&#xff0c;会向父进程发送一个SIGCHLD信号。父进程需要捕捉这个信号&#xff0c;并通过调用wait()或waitpid()等函数来回收子进程的资源。这一过程被称为“回收僵尸进程”。 如果父进程没有及时调用w…...

Docker 不再难懂:快速掌握容器命令与架构原理

1. Docker 是容器技术的一种 容器&#xff08;Container&#xff09;概述 容器&#xff08;Container&#xff09;是一种轻量级的虚拟化技术&#xff0c;它将应用程序及其所有依赖环境打包在一个独立的、可移植的运行时环境中。容器通过操作系统级的虚拟化提供隔离&#xff0…...

取消票证会把指定的票证从数据库中删除,同时也会把票证和航班 等相关表中的关联关系一起删除。但在删除之前,它会先检查当前用户是否拥有这张票

在做航班智能客服问答系统时会遇到取消票证的场景&#xff0c;这里涉及数据库的操作时会把指定的票证从数据库中删除&#xff0c;同时也会把票证和航班等相关表中的关联关系一起删除。但在删除之前&#xff0c;需要先检查当前用户是否拥有这张票&#xff0c;只有票主才有权限取…...

力扣-贪心-763 划分字母区间

思路 先统计字符串中每一个字母出现的最后下标&#xff0c;然后从end初始化为第一个字母出现的最后下标&#xff0c;在i<end时&#xff0c;不断更新end&#xff0c;因为一旦囊括新的字母就最起码要遍历到新字母出现的最后下标&#xff0c;在i>end时&#xff0c;说明遍历…...

【Redis 原理】网络模型

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

cpp中的继承

一、继承概念 在cpp中&#xff0c;封装、继承、多态是面向对象的三大特性。这里的继承就是允许已经存在的类&#xff08;也就是基类&#xff09;的基础上创建新类&#xff08;派生类或者子类&#xff09;&#xff0c;从而实现代码的复用。 如上图所示&#xff0c;Person是基类&…...

DeepSeek全栈接入指南:从零到生产环境的深度实践

第一章:DeepSeek技术体系全景解析 1.1 认知DeepSeek技术生态 DeepSeek作为新一代人工智能技术平台,构建了覆盖算法开发、模型训练、服务部署的全链路技术栈。其核心能力体现在: 1.1.1 多模态智能引擎 自然语言处理:支持文本生成(NLG)、语义理解(NLU)、情感分析等计算…...

CSS 真的会阻塞文档解析吗?

在网页开发领域&#xff0c;一个常见的疑问是 CSS 是否会阻塞文档解析。理解这一问题对于优化网页性能、提升用户体验至关重要。要深入解答这个问题&#xff0c;需要从浏览器渲染网页的原理说起。 浏览器渲染网页的基本流程 浏览器在接收到 HTML 文档后&#xff0c;会依次进行…...

大模型的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 专栏介绍 &#x1f525;课设、毕设、创新竞赛必备&#xff01;&#x1f525;本专栏涉及更高阶的运动规划算法轨迹优化实战&#xff0c;包括&#xff1a;曲线…...

Python 基本数据类型

目录 1. 字符串&#xff08;String&#xff09; 2. 列表&#xff08;List&#xff09; 3. 字典&#xff08;Dictionary&#xff09; 4. 集合&#xff08;Set&#xff09; 5. 数字&#xff08;Number&#xff09; 6. 布尔值&#xff08;Boolean&#xff09; 1. 字符串&…...

突破“第一崇拜“:五维心理重构之路

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

KubeKey一键安装部署k8s集群和KubeSphere详细教程

目录 一、KubeKey简介 二、k8s集群KubeSphere安装 集群规划 硬件要求 Kubernetes支持版本 操作系统要求 SSH免密登录 配置集群时钟 所有节点安装依赖 安装docker DNS要求 存储要求 下载 KubeKey 验证KubeKey 配置集群文件 安装集群 验证命令 登录页面 一、Ku…...

UE5网络通信架构解析

文章目录 前言一、客户端-服务器架构&#xff08;C/S Model&#xff09;二、对等网络架构&#xff08;P2P&#xff0c;非原生支持&#xff09;三、混合架构&#xff08;自定义扩展&#xff09;四、UE5网络核心机制 前言 UE5的网络通信主要基于客户端-服务器&#xff08;C/S&am…...

实验3 知识表示与推理

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

基于Springboot银行信用卡额度管理系统【附源码】

基于Springboot银行信用卡额度管理系统 效果如下&#xff1a; 系统登陆页面 用户个人中心页面 新增信用卡申请页面 评估审核页面 管理员主页面 评估审核页面 操作日志管理页面 消费页面 研究背景 随着金融行业的快速发展和信息技术的不断进步&#xff0c;信用卡作为一种便捷…...

达梦数据库学习笔记@1

目录 达梦数据库学习笔记一、表空间管理&#xff08;一&#xff09;默认表空间&#xff08;二&#xff09;相关数据字典&#xff08;三&#xff09;表空间操作&#xff08;四&#xff09;临时表空间管理 二、重做日志管理&#xff08;一&#xff09;系统视图&#xff08;二&…...

Markdown图片排版救星:5分钟搞定自适应大小和响应式布局(附CSS片段)

Markdown图片排版救星&#xff1a;5分钟搞定自适应大小和响应式布局&#xff08;附CSS片段&#xff09; 在技术写作的世界里&#xff0c;Markdown因其简洁高效而备受青睐。但当我们试图在Markdown文档中插入图片时&#xff0c;往往会遇到一个尴尬的现实&#xff1a;默认的图片处…...

解锁硬件潜能:Universal x86 Tuning Utility 让你的电脑性能全面释放

解锁硬件潜能&#xff1a;Universal x86 Tuning Utility 让你的电脑性能全面释放 【免费下载链接】Universal-x86-Tuning-Utility Unlock the full potential of your Intel/AMD based device. 项目地址: https://gitcode.com/gh_mirrors/un/Universal-x86-Tuning-Utility …...

MySQL升级如何回滚到旧版本_灾难恢复方案与快照备份恢复

MySQL升级后不可直接卸载重装旧版本&#xff0c;因数据字典、系统库结构、InnoDB redo log格式等已被新版本改写&#xff1b;唯一可行回滚路径是用旧版mysqld启动升级前备份的干净副本并切流&#xff0c;且备份须满足&#xff1a;①导出含--routines --events --triggers --sin…...

如何用ModAssistant快速解决Beat Saber模组安装的3大痛点

如何用ModAssistant快速解决Beat Saber模组安装的3大痛点 【免费下载链接】ModAssistant Simple Beat Saber Mod Installer 项目地址: https://gitcode.com/gh_mirrors/mo/ModAssistant 你是否曾因Beat Saber模组安装的复杂依赖关系而头痛&#xff1f;是否遇到过版本冲突…...

构建高效数字图书馆:fanqienovel-downloader开源工具的深度解析与实战指南

构建高效数字图书馆&#xff1a;fanqienovel-downloader开源工具的深度解析与实战指南 【免费下载链接】fanqienovel-downloader 下载番茄小说 项目地址: https://gitcode.com/gh_mirrors/fa/fanqienovel-downloader 在数字阅读时代&#xff0c;如何永久保存心爱的番茄小…...

Livox Avia雷达实测:450米远距与70°大FOV,在无人机测绘中到底有多香?

Livox Avia雷达实测&#xff1a;450米远距与70大FOV如何重塑无人机测绘体验 当无人机搭载的激光雷达在300米高空依然能清晰捕捉到高压电线的细微振动&#xff0c;当单次飞行即可完成整片林区的三维建模——这正是Livox Avia带给测绘工程师的真实工作变革。这款面阵激光雷达用45…...

计算机网络的计算模式

计算模式指的是网络中计算任务&#xff08;数据处理、存储、运算等&#xff09;在客户端和服务器之间如何分配与协作。随着技术发展&#xff0c;主要经历了以下几种模式的演变。一、计算模式的主要类型模式核心特点处理位置典型代表集中式计算模式所有计算在主机完成&#xff0…...

WebLaTeX:在线LaTeX编辑新体验,告别繁琐配置的写作利器

WebLaTeX&#xff1a;在线LaTeX编辑新体验&#xff0c;告别繁琐配置的写作利器 【免费下载链接】WebLaTex A complete alternative for Overleaf with VSCode Web Git Integration Copilot Grammar & Spell Checker Live Collaboration Support. Based on GitHub Code…...

AGI自主学习不是“试错”,而是“推演”——基于17万小时仿真数据的认知跃迁模型

第一章&#xff1a;AGI自主学习不是“试错”&#xff0c;而是“推演”——基于17万小时仿真数据的认知跃迁模型 2026奇点智能技术大会(https://ml-summit.org) 传统强化学习依赖海量环境交互与稀疏奖励信号&#xff0c;本质上是统计意义上的试错收敛&#xff1b;而新一代AGI认…...

Cold Diffusion:超越高斯噪声的通用图像生成框架解析

1. Cold Diffusion的核心思想&#xff1a;从噪声依赖到通用框架 第一次看到Cold Diffusion论文时&#xff0c;我正被传统扩散模型对高斯噪声的强依赖性困扰着。当时在做一个艺术风格转换项目&#xff0c;发现用标准DDPM处理非自然图像时效果总是不稳定。Cold Diffusion的出现就…...