C++【STL】改造红黑树简单模拟实现set map(带你了解set map的底层实现结构)

目录

一、学前铺垫(泛型编程)

二、改造红黑树

1.红黑树节点的改造

2.insert的改造

3.迭代器的实现

4.完整改造代码

三、set的模拟实现封装

四、map的模拟实现封装

五、完结撒❀

前言:

下面为了简单模拟实现set map所出现的代码是以C++中STL源码库中的代码逻辑基础进行的简化代码,本片博客目的是带你简单深入底层,了解set map底层的实现逻辑,对泛型编程有更加深刻的认识。


一、学前铺垫(泛型编程)

本篇博客我们通过对一个红黑树进行改造,使其可以让set和map的模拟实现都使用这一个红黑树结构,因为set map所存储的数据类型不一样,set底层存储的是pair<key,key>,map底层存储的是pair<key,value>,所以这里就一定会用上多个模板对红黑树进行改造,形成泛型编程,之后再对set map使用改造后的红黑树进行封装,达到模拟STL库中set map的效果。(有能力的可以直接去看STL中set map所实现的源码,逻辑与我所讲述的相同)

二、改造红黑树

1.红黑树节点的改造

这里节点的构造基本与二叉搜索树的节点构造相同,但是因为要同时兼顾set map两中类型,所以存储数据的类型不可以写死,要用到模板,节点代码如下:

template <class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;T _data;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};

2.insert的改造

需要改造的地方:

1) 根据STL库中的set和map的insert的功能,插入成功返回插入位置所在的迭代器以及true,插入失败说明树中已存在改值,返回该值所在的位置的迭代器以及false,所以返回类型应为pair<iterator,bool>,所以返回类型需要进行改造。

2) 在insert中大小值的比较,因为要兼容set和map,而在set中的模板类型只需要一个就可以进行初始化,因为set中底层数据类型是一样的,而map不同,map底层类型其实是pair<key,pair<key,value>>实现,因为在实现find函数时需要用到key的值并且与set保持一致,所以将value类型定义为pair<key,value>,那么在后续的比较大小中就不能那么随意了,因为set直接拿其节点指向的_data进行比较即可,而map中的_data为pair<key,pair<key,value>>,不可以直接拿来进行比较,所以我们将代码进行改造。

下面是模拟实现set,map的简单封装。SetKeyOFT,MapKeyOFT就是解决大小比较所定义的内部类。
Mymap.h:

template <class K,class V>
class map
{
public:struct MapKeyOFT{const K& operator()(const pair<K, V>& kv){return kv.first;}};
private:RBTree<K, pair<K,V>, MapKeyOFT> _t;
};

Myset.h:

template <class K>
class set
{
public:struct SetKeyOFT{const K& operator()(const K& key){return key;}};
private:RBTree<K,K, SetKeyOFT> _t;
};

insert函数改造实现:

	pair<iterator,bool> Insert(const T& data){//二叉树为空,插入第一个值if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root),true);}KeyOfT kot;//后续插入Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else{//不允许冗余return make_pair(iterator(cur),false);}}//找到对应位置cur = new Node(data);cur->_parent = parent;Node* newcur = cur;if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}//父亲的颜色是黑色也就结束while (parent && parent->_col == RED)//红黑树出现错误需要改正{Node* grandfather = parent->_parent;if (grandfather->_left == parent){//舅子树在右边Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){//存在且颜色为红parent->_col = BLACK;uncle->_col = BLACK;/*if (grandfather == _root){grandfather->_col = BLACK;}else{grandfather->_col = RED;}*/grandfather->_col = RED;cur = grandfather;parent = grandfather->_parent;}else{//舅子树不存在或颜色为黑if (parent->_left == cur){//单旋parent->_col = BLACK;grandfather->_col = RED;RNode(grandfather);}else{//双旋 先左再右LNode(parent);cur->_col = BLACK;grandfather->_col = RED;RNode(grandfather);}break;}}else{//舅子树在左边Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = grandfather->_parent;}else{if (parent->_right == cur){//单旋parent->_col = BLACK;grandfather->_col = RED;LNode(grandfather);}else{//双旋RNode(parent);LNode(grandfather);grandfather->_col = RED;cur->_col = BLACK;}break;}}}_root->_col = BLACK;return make_pair(iterator(newcur),true);}

因为只在红黑树insert函数里面使用的都是模板,所以是不知道所传数据的具体类型,但是在模拟实现的set,map中知道其对应的类型,所以我们可以在set,map类里面定义一个类,在这个类里面定义一个仿函数用于提取所对应比较大小的值,再将这个类用模板参数传递给红黑树中,在需要比较大小时提前用这个类定义一个变量,在通过仿函数进行大小的比较,这样就可以实现set,map的兼容。

3.迭代器的实现

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代
器,需要考虑以前问题:
● begin() end()
STL 明确规定, begin() end() 代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,
可以得到一个有序的序列,因此: begin() 可以放在红黑树中最小节点 ( 即最左侧节点 ) 的位
end() 放在最大节点 ( 最右侧节点 ) 的下一个位置 ,关键是最大节点的下一个位置在哪块?
能否给成 nullptr 呢?答案是行不通的,因为 end() 位置的迭代器进行 -- 操作,必须要能找最
后一个元素 ,此处就不行,因此最好的方式是 end() 放在头结点的位置

operator++()operator--()

// 找迭代器的下一个节点,下一个节点肯定比其大
void Increasement()
{//分两种情况讨论:_pNode的右子树存在和不存在// 右子树存在if (_pNode->_pRight){// 右子树中最小的节点,即右子树中最左侧节点_pNode = _pNode->_pRight;while (_pNode->_pLeft)_pNode = _pNode->_pLeft;}else{// 右子树不存在,向上查找,直到_pNode != pParent->rightPNode pParent = _pNode->_pParent;while (pParent->_pRight == _pNode){_pNode = pParent;pParent = _pNode->_pParent;}// 特殊情况:根节点没有右子树if (_pNode->_pRight != pParent)_pNode = pParent;}
}
// 获取迭代器指向节点的前一个节点
void Decreasement()
{//分三种情况讨论:_pNode 在head的位置,_pNode 左子树存在,_pNode 左子树不存在// 1. _pNode 在head的位置,--应该将_pNode放在红黑树中最大节点的位置if (_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)_pNode = _pNode->_pRight;else if (_pNode->_pLeft){// 2. _pNode的左子树存在,在左子树中找最大的节点,即左子树中最右侧节点_pNode = _pNode->_pLeft;while (_pNode->_pRight)_pNode = _pNode->_pRight;}else{// _pNode的左子树不存在,只能向上找PNode pParent = _pNode->_pParent;while (_pNode == pParent->_pLeft){_pNode = pParent;pParent = _pNode->_pParent;}_pNode = pParent;}
}

4.完整改造代码

#pragma once
#include <iostream>
using namespace::std;enum Color
{RED,//(0)BLACK//(1)
};template <class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;T _data;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};template <class T,class Ref,class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator--(){if (_node->_left){//存在左子树Node* cur = _node->_left;while (cur->_right){cur = cur->_right;}_node = cur;}else{//不存在左子树Node* parent = _node->_parent;if (parent && parent->_left == _node){//为父亲的左孩子while (parent && parent->_left == _node){_node = parent;parent = parent->_parent;}_node = parent;}else{//为父亲的右孩子_node = parent;}}return *this;}Self& operator++(){if (_node->_right){//存在右子树_node = _node->_right;while (_node && _node->_left){_node = _node->_left;}}else{//不存在右子树Node* parent = _node->_parent;while (parent && _node == parent->_right){_node = parent;parent = parent->_parent;}_node = parent;}return *this;}
};template <class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __RBTreeIterator<T, T&, T*> iterator;typedef __RBTreeIterator<T,const T&,const T*> const_iterator;RBTree() = default;//强制编译器生成构造函数//拷贝构造RBTree(RBTree<K, const T, KeyOfT>& t){_root = copy(t._root);}iterator begin(){Node* LeftMin = _root;while (LeftMin && LeftMin->_left){LeftMin = LeftMin->_left;}return iterator(LeftMin);}iterator end(){return iterator(_root->_parent);}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);}//右单旋,满足二叉树引发右单旋之后平衡因子一定为0void RNode(Node* parent){Node* pparent = parent->_parent;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;parent->_parent = subL;if (pparent){subL->_parent = pparent;if (pparent->_left == parent){pparent->_left = subL;}else{pparent->_right = subL;}}else{_root = subL;subL->_parent = nullptr;}}//左单旋void LNode(Node* parent){Node* pparent = parent->_parent;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (pparent){subR->_parent = pparent;if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;}}else{_root = subR;subR->_parent = nullptr;}}pair<iterator,bool> Insert(const T& data){//二叉树为空,插入第一个值if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root),true);}KeyOfT kot;//后续插入Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else{//不允许冗余return make_pair(iterator(cur),false);}}//找到对应位置cur = new Node(data);cur->_parent = parent;Node* newcur = cur;if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}//父亲的颜色是黑色也就结束while (parent && parent->_col == RED)//红黑树出现错误需要改正{Node* grandfather = parent->_parent;if (grandfather->_left == parent){//舅子树在右边Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){//存在且颜色为红parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = grandfather->_parent;}else{//舅子树不存在或颜色为黑if (parent->_left == cur){//单旋parent->_col = BLACK;grandfather->_col = RED;RNode(grandfather);}else{//双旋 先左再右LNode(parent);cur->_col = BLACK;grandfather->_col = RED;RNode(grandfather);}break;}}else{//舅子树在左边Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = grandfather->_parent;}else{if (parent->_right == cur){//单旋parent->_col = BLACK;grandfather->_col = RED;LNode(grandfather);}else{//双旋RNode(parent);LNode(grandfather);grandfather->_col = RED;cur->_col = BLACK;}break;}}}_root->_col = BLACK;return make_pair(iterator(newcur),true);}bool IsRBTree(){if (_root->_col == RED){cout << "根节点为红节点" << endl;return false;}int DefNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){DefNum++;}cur = cur->_left;}return _Check(_root, 0, DefNum);}~RBTree(){Destory(_root); _root = nullptr;}private:Node* copy(const Node* root){if (root == nullptr){return nullptr;}Node* newnode = new Node(root->_data);newnode->_col = root->_col;newnode->_left = copy(root->_left);if(newnode->_left)newnode->_left->_parent = newnode;newnode->_right = copy(root->_right);if (newnode->_left)newnode->_left->_parent = newnode;return newnode;}void Destory(Node* root){if (root == nullptr){return;}Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}bool _Check(Node* root, int BlackNum, int DefNum){if (root == nullptr){if (BlackNum != DefNum){cout << BlackNum << "|" << DefNum << endl;cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "->存在连续的两个红节点" << endl;return false;}if (root->_col == BLACK){BlackNum++;}return _Check(root->_left, BlackNum, DefNum)&& _Check(root->_right, BlackNum, DefNum);}Node* _root = nullptr;};

三、set的模拟实现封装

set 的底层为红黑树,因此只需在 set 内部封装一棵红黑树,即可将该容器实现出来 ( 具体实现可参
map)
template <class K>
class set
{
public:struct SetKeyOFT{const K& operator()(const K& key){return key;}};typedef typename RBTree<K, K, SetKeyOFT>::iterator iterator;typedef typename RBTree<K, const K, SetKeyOFT>::iterator const_iterator;pair<iterator,bool> insert(const K& key){ return _t.Insert(key);}iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin() const{return _t.begin();}const_iterator end() const{return _t.end();}private:RBTree<K, const K, SetKeyOFT> _t;
};

四、map的模拟实现封装

template <class K,class V>
class map
{
public:struct MapKeyOFT{const K& operator()(const pair<K, V>& kv){return kv.first;}};typedef typename RBTree<K, pair<const K, V>, MapKeyOFT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOFT>::iterator const_iterator;V& operator[](const K& key){pair<iterator,bool> ret = _t.Insert(make_pair(key,V()));return ret.first->second;}pair<iterator,bool> insert(const pair<K,V>& kv){return _t.Insert(kv);}iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin() const{return _t.begin();}const_iterator end() const{return _t.end();}private:RBTree<K, pair<const K,V>, MapKeyOFT> _t;
};

五、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤

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

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

相关文章

Tensorflow入门实战 P03-天气识别

目录 1、完整代码 2、运行结果 2.1 查看20张图片 2.2 程序运行 2.3 运行结果 3、小结 ① 代码运行过程中有报错&#xff1a; ② 修改代码如下&#xff1a; ③ 分析原因&#xff1a; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&…

NDIS Filter开发-PNP响应和安装

NDIS filter驱动可能是最容易生成的驱动之一&#xff0c;如果你安装了VS 2015 WDK之后&#xff0c;你可以直接生成一个能运行的Filter驱动&#xff0c;它一般是ndislwf。 和大部分硬件不同&#xff0c;NDIS Filter驱动介于软件和硬件抽象层之上&#xff0c;它和硬件相关&…

SpringCloud Gateway中Route Predicate Factories详细说明

官网地址&#xff1a;https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gateway-request-predicates-factories Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。 Spring Cloud Gateway …

使用 GPT-4 创作高考作文 2024年

使用 GPT-4 创作高考作文 2024年 使用 GPT-4 创作高考作文&#xff1a;技术博客指南 &#x1f914;✨摘要引言正文内容&#xff08;详细介绍&#xff09; &#x1f4da;&#x1f4a1;什么是 GPT-4&#xff1f;高考作文题目分析 ✍️&#x1f9d0;新课标I卷 人类智慧的进步&…

【C51】C51单片机实现的 抽奖机 设计与编程指南

文章目录 前言&#xff1a;1. 实现效果2. 准备工作3. 编写代码总结&#xff1a; 前言&#xff1a; 在本文中&#xff0c;我们将介绍如何使用C51单片机来实现一个简单的抽奖机。这个项目不仅能够展示C51单片机的基本应用&#xff0c;还能让我们了解如何通过编程来控制硬件&…

9.3 Go 接口的多态性

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Django 视图类

在Django框架中&#xff0c;视图类&#xff08;Class-based views&#xff0c;简称CBVs&#xff09;提供了一个面向对象的方式来定义视图。这种方式可以让你通过创建类来组织视图逻辑&#xff0c;而不是使用基于函数的视图&#xff08;Function-based views&#xff0c;简称FBV…

详解FedAvg:联邦学习的开山之作

FedAvg&#xff1a;2017年 开山之作 论文地址&#xff1a;https://proceedings.mlr.press/v54/mcmahan17a/mcmahan17a.pdf 源码地址&#xff1a;https://github.com/shaoxiongji/federated-learning 针对的问题&#xff1a;移动设备中有大量的数据&#xff0c;但显然我们不能收…

minio的一个基础使用案例:用户头像上传

文章目录 一、minio下载安装&#xff08;Windows&#xff09;二、案例需求分析三、后端接口开发 一、minio下载安装&#xff08;Windows&#xff09; 1. 下载minio服务端和客户端 minio下载地址 2. 手动搭建目录 /minio/binmc.exeminio.exe/data/logs手动创建minio应用程序目…

vivado HW_DEVICE

硬件设备 描述 在Vivado Design Suite的硬件管理器功能中&#xff0c;每个硬件目标都可以 具有一个或多个Xilinx FPGA设备进行编程或用于调试目的。这个 hw_device对象是通过hw_server打开的hw_target上的物理部分。这个 current_hw_device命令指定或返回当前设备。 相关对象 硬…

Linux—小小内核升级

本篇主要是讲述下关于内核的一些基本常识&#xff0c;并记录下内核升级和编译的过程&#xff0c;若有遗漏/有误之处&#xff0c;望各位大佬们指出。 Ⅰ 基本内核常识 常见内核安装包 内核(kernel)&#xff1a;这是Linux操作系统的核心部分&#xff0c;它负责管理系统的硬件和…

Vue3学习第二天记录

Vue3学习第二天记录 背景说明截图记录一个简单的JS文件Vue3的watch()函数Vue3的toRef()/toRefs()函数前端数据类型的分类前端写一个对外暴露的函数前端的...语法Vue3中watch()函数的总结Vue3中watchEffect()函数Vue3中watch()函数的坑Vue3中computed()函数 背景 最近在学习尚硅…

Vue2入门(安装Vue、devtools,创建Vue)以及MVVM分层思想

文章目录 1.下载并安装Vue2.使用Vue2.1 创建Vue以及挂载Vue2.2 模板语句的数据来源&#xff1a;data2.3 template配置项详解2.4 Vue实例和容器的关系 3.安装devtools4.MVVM分层思想5.通过vm可以访问哪些属性 1.下载并安装Vue &#xff08;1&#xff09;Vue是一个基于JavaScrip…

搭建高可用k8s

高可用只针对于api-server&#xff0c;需要用到nginx keepalived&#xff0c;nginx提供4层负载&#xff0c;keepalived提供vip(虚拟IP) 系统采用openEuler 22.03 LTS 1. 前期准备 因为机器内存只有16G&#xff0c;所有我采用3master 1node 1.1 修改主机配置&#xff08;所有节…

扩散模型会成为深度学习的下一个前沿领域吗?

文章目录 一、说明二、 第 1 部分&#xff1a;了解扩散模型2.1 什么是扩散模型2.2 正向扩散2.3 反向扩散 三、他们的高成本四、扩散模型的用处五、为什么扩散模型如此出色六、第 2 部分&#xff1a;使用扩散模型生成6.1 用于自然语言处理和 LLM 的文本扩散6.2 音频视频生成6.3 …

下载安装Thonny并烧录MicroPython固件至ESP32

Thonny介绍 一、Thonny的基本特点 面向初学者&#xff1a;Thonny的设计初衷是为了帮助Python初学者更轻松、更快速地入门编程。它提供了直观易懂的用户界面和丰富的功能&#xff0c;降低了编程的门槛。轻量级&#xff1a;作为一款轻量级的IDE&#xff0c;Thonny不会占用过多的…

RDK X3(aarch64) 测试激光雷达思岚A1

0. 环境 - 亚博智能的ROSMASTER-X3 - RDK X3 1.0 0.1 资料 文档资料 https://www.slamtec.com/cn/Support#rplidar-a-series SDK https://github.com/slamtec/rplidar_sdk ROS https://github.com/slamtec/rplidar_ros https://github.com/Slamtec/sllidar_ros2 1. robostu…

1310. 子数组异或查询 异或 前缀和 python

有一个正整数数组 arr&#xff0c;现给你一个对应的查询数组 queries&#xff0c;其中 queries[i] [Li, Ri]。 对于每个查询 i&#xff0c;请你计算从 Li 到 Ri 的 XOR 值&#xff08;即 arr[Li] xor arr[Li1] xor ... xor arr[Ri]&#xff09;作为本次查询的结果。 并返回一…

《精通ChatGPT:从入门到大师的Prompt指南》附录B:推荐阅读资源

作者&#xff1a;斯图尔特拉塞尔 (Stuart Russell) 和 彼得诺维格 (Peter Norvig) 简介&#xff1a;这本书被誉为人工智能领域的经典教材&#xff0c;内容涵盖了AI的基本原理、算法及其应用。无论是入门者还是专业研究者&#xff0c;都能从中获得启发。 2. 《深度学习》 作者…

【目标跟踪网络训练 Market-1501 数据集】DeepSort 训练自己的跟踪网络模型

前言 Deepsort之所以可以大量避免IDSwitch&#xff0c;是因为Deepsort算法中特征提取网络可以将目标检测框中的特征提取出来并保存&#xff0c;在目标被遮挡后又从新出现后&#xff0c;利用前后的特征对比可以将遮挡的后又出现的目标和遮挡之前的追踪的目标重新找到&#xff0…