【C++高阶】高效数据存储:理解并模拟实现红黑树Map与Set

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C++ “ 登神长阶 ”
🤡往期回顾🤡:了解 红黑树
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀模拟实现Map与Set

  • 📒1. 改造红黑树
  • 📜2. 红黑树的迭代器
    • 🌞迭代器基本设计
    • 🌙begin()与end()
    • ⭐operator++()与operator--()
  • 📚3. Set的模拟实现
    • 🧩Set的基本设计
  • 📝4. Map的模拟实现
    • 🧩Map的基本设计
  • 📖5. 总结


前言: 在编程的浩瀚宇宙中,数据结构作为构建程序的基石,扮演着至关重要的角色。它们不仅定义了数据的存储方式,还极大地影响着程序的性能与效率。在众多经典数据结构中,Map(映射)和Set(集合)以其独特的性质和广泛的应用场景,成为了程序员们手中不可或缺的工具。Map允许我们根据键(Key)快速检索值(Value),而Set则提供了一种不包含重复元素的数据集合

深入理解并熟练掌握这些高级数据结构,并非一蹴而就。为了更加深刻地认识Map与Set的工作原理,以及它们背后隐藏的算法智慧,一个行之有效的方法便是亲手模拟实现它们。这一过程,不仅能够帮助我们加深对数据结构的理解,还能在实践中锻炼编程能力和问题解决能力

本文旨在为读者提供一个全面而深入的视角,通过逐步解析红黑树的基本原理、详细阐述模拟实现的过程,并辅以丰富的代码示例,帮助读者掌握红黑树Map与Set的构建与使用。我们将从最基本的二叉搜索树出发,逐步引入红黑树的特性和规则

让我们一起踏上学习的旅程,探索它带来的无尽可能!


📒1. 改造红黑树

改造红黑树以适配map和set容器,主要涉及到如何根据这两种容器的特性来设计和实现红黑树节点的存储以及相应的操作。map和set的主要区别在于它们存储的元素类型:map存储键值对(key-value pairs),而set仅存储唯一的键值(通常是键本身作为值)。尽管如此,它们在底层数据结构(如红黑树)的实现上有很多相似之处

改造内容:

  • K:key的类型
  • T:如果是map,则为pair<K, V>; 如果是set,则为K
  • KeyOfT:通过T来获取key的一个仿函数类
// Map 与 Set
// set -> RBTree<K, K, SetKeyOfT> _t;
// map -> RBTree<K, pair<const K, V, MapKeyOfT>> _t;

红黑树的节点设计:

enum Color
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;// pair<K, V> _kv; // 上节内容使用的这种方式T _data;Color _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};

红黑树类的实现参考上一篇文章:红黑树类的实现
与红黑树类的不同的是Insert(插入)的类型是pair<iterator, bool>或者pair<Node*, bool>,然后还要修改内部的return返回的对象
在这里插入图片描述

在这里插入图片描述

以pair<Node*, bool>为例
代码示例:

pair<Node*, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* parent = nullptr;Node* cur = _root;// 通过仿函数KeyOfT来比较数据的大小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->_col = RED;// 旋转变色return make_pair(newnode, true);;}

对于set,你可以简单地使用RBTree<K, K, SetKeyOfT>;而对于map,则使用RBTree<K, pair<const K, V>, MapKeyOfT>


📜2. 红黑树的迭代器

🌞迭代器基本设计

// 通过模板来达到const的迭代器的复用
template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef RBTreeNode<T> Node;typedef __TreeIterator<T, Ref, Ptr> Self;Node* _node;// 构造函数__TreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &(_node->_data);}bool operator!=(const Self& s){return _node != s._node;}
};

🌙begin()与end()

STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,
可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位
置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?
能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行–操作,必须要能找最
后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置

在这里插入图片描述
代码示例(C++):
(注意:此步需要在RBTree类的内部实现),以便map,set的使用

typedef __TreeIterator<T, T&, T*> iterator;
typedef __TreeIterator<T, const T&, const T*> const_iterator;iterator begin()
{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);
}iterator end()
{return iterator(nullptr);
}const_iterator begin() const
{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);
}const_iterator end() const
{return const_iterator(nullptr);
}

⭐operator++()与operator–()

operator++()

  • 右不为空,右子树的最左节点
  • 右为空,沿着到根的路径找孩子是父亲左的那个祖先

operator–()

  • 左不为空,左子树的最右节点
  • 左为空,沿着到根的路径找孩子是父亲右的那个祖先

注意:++和–正好是完全相反的


代码示例(C++):

Self& operator++()
{if (_node->_right){Node* cur = _node->_right;while (cur->_left){cur = cur->_left;}_node = cur;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}Self& operator--()
{if (_node->_left){Node* cur = _node->_left;while (cur->_right){cur = cur->_right;}_node = cur;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent&& cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}

📚3. Set的模拟实现

🧩Set的基本设计

template<class K>
class set
{
public:// SetKeyOfT:通过T来获取key的一个仿函数类struct SetKeyOfT{const K& operator()(const K& key){return key;}};// typename告诉编译器这是类型,// 因为set不能够进行修改,所以我们用const迭代器来初始化普通迭代器,来达到不能修改的目的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();}// 复用RBTree中的插入pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;
};

📝4. Map的模拟实现

🧩Map的基本设计

template<class K, class V>
class map
{
public:// MapKeyOfT:通过pair来获取kv.first的一个仿函数类struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};// map是一个key不能修改,value能够修改的结构typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}// 重载map中的operatorp[]// operatorp[] 当原数据中没有时,插入并初始化,有则修改secondV& operator[](const K& key){// 没有是插入,有则是查找pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}// 复用RBTree中的插入pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

📖5. 总结

随着我们对红黑树Map与Set的深入探索与模拟实现,这场高效数据存储的旅程也即将画上圆满的句号。回望这段旅程,我们从红黑树的基本概念出发,逐步揭示了其保持平衡、实现高效操作的秘密,并通过亲手编写代码,将理论转化为了实践

同时,我们也要意识到,数据结构的选择并非一成不变。在实际应用中,我们需要根据具体需求、数据规模、性能要求等因素综合考虑,选择最合适的数据结构来解决问题。只有这样,我们才能真正做到高效、稳定地处理数据。

然而,学习之路永无止境。红黑树只是众多高效数据结构中的一种,它们各自有着独特的优势和适用场景。在未来的学习与实践中,我们还需要继续探索其他数据结构,如AVL树、B树、B+树等,以拓宽我们的视野,提升我们的技能

希望各位在未来的学习与工作中,保持对知识的渴望与追求,勇于挑战自我,不断探索未知领域。相信在不久的将来,你们定能在数据处理的广阔舞台上大放异彩

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【Android】kotlin jdk版本冲突与Kotlin依赖管理插件

1、androidx.activity&#xff1a;activity&#xff1a;1.8.0 依赖版本错误问题 *依赖项“androidx.activity&#xff1a;activity&#xff1a;1.8.0”要求依赖它的库和应用针对版本 34 或更高版本 Android API 进行编译。&#xff1a;app 目前是针对 android-33 编译的。此外…

收银系统源代码-收银端UI风格

智慧新零售收银系统是一套线下线上一体化收银系统&#xff0c;给商户提供含线下收银称重、线上商城、精细化会员管理、ERP进销存、丰富营销活动、移动店务助手等一体化的解决方案。 如Windows版收银&#xff08;exe安装包&#xff09;、安卓版收银&#xff08;apk安装包&#…

LabVIEW平台从离散光子到连续光子的光子计数技术

光子计数技术用于将输入光子数转换为离散脉冲。常见的光子计数器假设光子是离散到达的&#xff0c;记录到来的每一个光子。但是&#xff0c;当两个或多个光子同时到达时&#xff0c;计数器会将其记录为单个脉冲&#xff0c;从而只计数一次。当连续光子到达时&#xff0c;离散光…

Monorepo仓库管理策略之 Lerna

这里写目录标题 前言&#xff1a;一、简介二、新建项目使用安装生成结构 三、复用现有项目执行命令查看包 四、配置package相互引用导入现有的包 五、发布包确定项目版本发布项目添加项目到到git发布包到NPM包发布出错解决方案 五、实例代码 前言&#xff1a; 将大型代码仓库分…

【漏洞复现】通达OA v2017 video_file.php 任意文件下载漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

【鸿蒙学习笔记】Stage模型

官方文档&#xff1a;Stage模型开发概述 目录标题 Stage模型好处Stage模型概念图ContextAbilityStageUIAbility组件和ExtensionAbility组件WindowStage Stage模型-组件模型Stage模型-进程模型Stage模型-ArkTS线程模型和任务模型关于任务模型&#xff0c;我们先来了解一下什么是…

数学建模及国赛

认识数学建模及国赛 认识数学建模 环境类&#xff1a;预测一下明天的气温 实证类&#xff1a; 评价一下政策的优缺点 农业类&#xff1a; 预测一下小麦的产量 财经类&#xff1a; 分析一下理财产品的最优组合 规划类&#xff1a; 土地利用情况进行 合理的划分 力学类&#xf…

RedHat Linux8 修改root管理员账户密码命令

RedHat Linux8 修改root管理员账户密码命令&#xff1a; sudo passwd root RedHat重置root管理员密码&#xff1a; 1. 查看Linux系统版本信息 cat /etc/redhat-release2. 重置密码 2.1 进入内核编辑界面 重启Linux系统并出现引导界面&#xff0c;按下键盘上的e键进入内…

Pyecharts绘制热力图的说明+代码实战

引言 热力图在数据可视化中是一种强大的工具&#xff0c;可以直观地展示数据的分布情况和变化趋势。Pyecharts是一个基于Echarts的Python可视化库&#xff0c;提供了丰富的图表类型&#xff0c;包括热力图。在本文中&#xff0c;我们将深入探讨Pyecharts绘制多种炫酷热力图的参…

12-阿里云单细胞处理-PBMC(by-jmzeng)

scRNA_10X/seurat-v2/sup-patient1-PBMC.Rmd at master jmzeng1314/scRNA_10X (github.com) s04-运行seurat流程处理一万个单细胞转录组数据并自动化出报告_哔哩哔哩_bilibili #section 3已更新#「生信技能树」单细胞公开课2021_哔哩哔哩_bilibili 上传读取数据 可以配置租…

手表名表维修开单系统软件教程,佳易王钟表养护维护保养记录开单软件操作教程

手表名表维修开单系统软件教程&#xff0c;佳易王钟表养护维护保养记录开单软件操作教程 以下软件操作教程以&#xff0c;佳易王钟表养护维修管理系统软件为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 一、软件程序操作教程 1、佳易王钟表维…

寂静孤独的404页面源码

寂静孤独的404页面源码&#xff0c;灯光闪烁动态效果&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 寂静孤独的404页面源…

【面试八股总结】面向对象三大特性、虚函数、纯虚函数、虚继承

参考资料&#xff1a;阿秀 一、面向对象三大特性 封装&#xff1a;将数据和代码捆绑在一起&#xff0c;避免外界干扰和不确定性访问 继承&#xff1a;让某种类型对象获得另一个类型对象的属性和方法 多态&#xff1a;同一种事务表现出不同事务的能力&#xff0c;即&#xf…

Git 详解(原理、使用)

git 快速上手请看这篇博客 Git 快速上手 1. 什么是 Git Git 是目前最主流的一个版本控制器&#xff0c;并且是分布式版本控制系统&#xff0c;可以控制电脑上所有格式的文档 版本控制器&#xff1a;记录每次修改以及版本迭代的管理系统 对于文本文件&#xff0c;可以记录每次…

Vue3项目如何使用npm link本地测试组件库

一、组件库操作 1、在组件库项目中先运行npm run lib&#xff0c;其效果如下 2、在组件库项目中在运行npm link&#xff0c;其效果如下 会创建一个全局的软连接指向本地的组件库 二、Vue3项目使用 1、在项目中运行 npm link 组件名称&#xff08;即&#xff1a;组件库packag…

【PB案例学习笔记】-30动态打开窗口

写在前面 这是PB案例学习笔记系列文章的第30篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

数电基础 - 数制,码制

目录 一. 简介 数制 码制 二. 进制 十进制&#xff08;Decimal&#xff09;&#xff1a; 二进制&#xff08;Binary&#xff09;&#xff1a; 八进制&#xff08;Octal&#xff09;&#xff1a; 十六进制&#xff08;Hexadecimal&#xff09;&#xff1a; 三. 进制的转…

Go语言---正则表达式

正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具。虽然正则表达式比纯粹的文本匹配效率低&#xff0c;但是它却更灵活。按照它的语法规则&#xff0c;只需构造出的匹配模式就能够从原始文本中筛选出几乎任何你想要得到的字符组合。go语言的通过regexp标准包来实现…

外卖商城平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;骑手管理&#xff0c;商品类型管理&#xff0c;商品信息管理&#xff0c;订单信息管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;商品信息&#…

理解负载组电路-EAK负载电路解释

负载组具有安全、可靠、操作方便、使用寿命长等特点。了解控制、冷却和负载元件电路的布局和功能对于理解负载组的运行、为应用选择负载组和维护负载组非常重要。以下各节将描述这些电路。 EAK负荷组运行概述 负载组接收来自电源的电力&#xff0c;将其转换为热量&#xff0c;…