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

 

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

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

相关文章

前端面试题之HTML篇

1.src和href的区别 src用于替换当前元素&#xff0c;href用于在当前文档和引用资源之间确立联系。 src可用于img、input、style、script、iframe---同步加载执行 href可用于link、a---异步 1.用途不同 src 用于引入外部资源&#xff0c;通常是图像、视频、JavaScript 文件等&am…

硬件工程师入门教程

1.欧姆定律 测电压并联使用万用表测电流串联使用万用表&#xff0c;红入黑出 2.电阻的阻值识别 直插电阻 贴片电阻 3.电阻的功率 4.电阻的限流作用 限流电阻阻值的计算 单位换算关系 5.电阻的分流功能 6.电阻的分压功能 7.电容 电容简单来说是两块不连通的导体加上中间的绝…

01背包之---应用篇

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、01背包之---背包是否能被装满&#xff1f;例题1.分析题意例题2.分析题意 二、01背包之---装满背包有多少种组合?例题1.分析题意 三、01背包之---容量为N的…

DeepSeek赋能智慧文旅:新一代解决方案,重构文旅发展的底层逻辑

DeepSeek作为一款前沿的人工智能大模型&#xff0c;凭借其强大的多模态理解、知识推理和内容生成能力&#xff0c;正在重构文旅产业的发展逻辑&#xff0c;推动行业从传统的经验驱动向数据驱动、从人力密集型向智能协同型转变。 一、智能服务重构&#xff1a;打造全域感知的智…

uniapp修改picker-view样式

解决问题&#xff1a; 1.选中文案样式&#xff0c;比如字体颜色 2.修改分割线颜色 3.多列时&#xff0c;修改两边间距让其平分 展示效果&#xff1a; 代码如下 <template><u-popup :show"showPicker" :safeAreaInsetBottom"false" close&quo…

开源嵌入式实时操作系统uC/OS-II介绍

一、uC/OS-II的诞生&#xff1a;从开源实验到行业标杆 背景与起源 uC/OS-II&#xff08;Micro-Controller Operating System Version II&#xff09;诞生于1992年&#xff0c;由嵌入式系统先驱Jean J. Labrosse开发。其前身uC/OS&#xff08;1991年&#xff09;最初作为教学工…

8.spring对logback的支持

文章目录 一、入口二、源码解析LoggingApplicationListener 三、其它支持四、总结 本节以logback为背景介绍的 一、入口 gav: org.springframework.boot:spring-boot:3.3.4 spring.factories文件中有如下两个配置 org.springframework.boot.logging.LoggingSystemFactory\ …

OpenHarmony分布式数据管理子系统

OpenHarmony分布式数据管理子系统 简介 目录 组件说明 分布式数据对象数据共享分布式数据服务Key-Value数据库首选项关系型数据库标准数据化通路 相关仓 简介 子系统介绍 分布式数据管理子系统支持单设备的各种结构化数据的持久化&#xff0c;以及跨设备之间数据的同步、…

智慧后勤的消防管理:豪越科技为安全护航

智慧后勤消防管理难题大揭秘&#xff01; 在智慧后勤发展得如火如荼的当下&#xff0c;消防管理却暗藏诸多难题。传统模式下&#xff0c;消防设施分布得那叫一个散&#xff0c;就像一盘散沙&#xff0c;管理起来超费劲。人工巡检不仅效率低&#xff0c;还容易遗漏&#xff0c;不…

python轻量级框架-flask

flask简述 Flask 是 Python 生态圈中一个基于 Python 的Web 框架。其轻量、模块化和易于扩展的特点导致其被广泛使用&#xff0c;适合快速开发 Web 应用以及构建小型到中型项目。它提供了开发 Web 应用最基础的工具和组件。之所以称为微框架&#xff0c;是因为它与一些大型 We…

政安晨【零基础玩转各类开源AI项目】DeepSeek 多模态大模型Janus-Pro-7B,本地部署!支持图像识别和图像生成

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 目录 下载项目 创建虚拟环境 安装项目依赖 安装 Gradio&#xff08;UI&#xff09; 运…

在 Mac mini M2 上本地部署 DeepSeek-R1:14B:使用 Ollama 和 Chatbox 的完整指南

随着人工智能技术的飞速发展&#xff0c;本地部署大型语言模型&#xff08;LLM&#xff09;已成为许多技术爱好者的热门选择。本地部署不仅能够保护隐私&#xff0c;还能提供更灵活的使用体验。本文将详细介绍如何在 Mac mini M2&#xff08;24GB 内存&#xff09;上部署 DeepS…

【Godot4.3】基于绘图函数的矢量蒙版效果与UV换算

概述 在设计圆角容器时突发奇想&#xff1a; 将圆角矩形的每个顶点坐标除以对应圆角矩形所在Rect2的size&#xff0c;就得到了顶点对应的UV坐标。然后使用draw_colored_polygon&#xff0c;便可以做到用图片填充圆角矩形的效果。而且这种计算的效果就是图片随着其填充的图像缩…

51单片机-AT24CXX存储器工作原理

1、AT24CXX存储器工作原理 1.1、特点&#xff1a; 与400KHz&#xff0c;I2C总线兼容1.8到6.0伏工作电压范围低功耗CMOS技术写保护功能当WP为高电平时进入写保护状态页写缓冲器自定时擦写周期100万次编程/擦除周期可保存数据100年8脚DIP SOIC或TSSOP封装温度范围商业级和工业级…

Linux网络 网络层

IP 协议 协议头格式 4 位版本号(version): 指定 IP 协议的版本, 对于 IPv4 来说, 就是 4. 4 位头部长度(header length): IP 头部的长度是多少个 32bit, 也就是 4 字节&#xff0c;4bit 表示最大的数字是 15, 因此 IP 头部最大长度是 60 字节. 8 位服务类型(Type Of Service):…

Unity百游修炼(1)——FootBall详细制作全流程

一、引言 游玩测试&#xff1a; Football 游玩测试 1.项目背景与动机 背景&#xff1a;在学习 Unity 的过程中&#xff0c;希望通过实际项目来巩固所学知识&#xff0c;同时出于对休闲小游戏的喜爱&#xff0c;决定开发一款简单有趣的小游戏加深自己的所学知识点。 动机&#…

C语言(13)------------>do-while循环

1.do-while循环的语法 我们知道C语言有三大结构&#xff0c;顺序、选择、循环。我们可以使用while循环、for循环、do-while循环实现循环结构。之前的博客中提及到了前两者的技术实现。可以参考&#xff1a; C语言&#xff08;11&#xff09;-------------&#xff1e;while循…

【1】VS Code 新建上位机项目---C#基础语法

VS Code 新建上位机项目---C#基础语法 1 基本概念1.1 准备工具1.2 新建项目2 C#编程基础2.1 命名空间和类2.2 数据类型2.3 控制台输入输出2.3.1 输入输出: write 与 read2.3.2 格式化 : string.Foramt() 与 $2.3.3 赋值与运算2.4 类型转换2.4.1 数值类型之间的转换:(int)2.4…

SQL:DQL数据查询语言以及系统函数(oracle)

SQL Structured Query Language&#xff0c;结构化查询语言, 是一种用于管理和操作关系数据库的标准编程语言。 sql的分类 DQL&#xff08;Data Query Language&#xff09;&#xff1a;数据查询语言 DDL&#xff08;Data Definition Language&#xff09;&#xff1a;数据…

从单片机的启动说起一个单片机到点灯发生了什么下——使用GPIO点一个灯

目录 前言 HAL库对GPIO的抽象 核心分析&#xff1a;HAL_GPIO_Init 前言 我们终于到达了熟悉的地方&#xff0c;对GPIO的初始化。经过漫长的铺垫&#xff0c;我们终于历经千辛万苦&#xff0c;来到了这里。关于GPIO的八种模式等更加详细的细节&#xff0c;由于只是点个灯&am…