【C++】使用红黑树进行封装map和set

🌇个人主页:平凡的小苏
📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘
🛸C++专栏C++内功修炼基地
> 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问,感谢你们的转发! 关注我,关注我,关注我,你们将会看到更多的优质内容!!

一、STL库中的set和map源码

本文难点:使用红黑树封装set和map,必须保证两种数据结构复用同一棵红黑树;且满足set和map的性质,set的value不可被改变,而map的value可以被改变。

在这里插入图片描述

二、利用模板区分map和set

template<class T>
struct RedBlackTreeNode
{T _data;RedBlackTreeNode<T>* _left;//该节点的左孩子RedBlackTreeNode<T>* _right;//该节点的右孩子RedBlackTreeNode<T>* _parent;//该节点是父亲节点Color _col;RedBlackTreeNode(const T& data): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}
};

map和set的区别在于value的不同,红黑树模板参数T,代表value用以区分set和map。

三、利用仿函数比较大小

我们会发现红黑树的插入等接口会对key值进行比较大小,像set直接对key进行比较,这没问题,但是map中的节点装的是pair<K,V>,pair的比较规则是first比完之后可能会再去比较second(而我们仅仅想要比较first,该比较规则不适用)。

通过源码启发,我们可以对红黑树新增一个模板参数:
仿函数KeyOfT,在set和map类中完善该仿函数的比较对象,
用于区分set和map的比较:

struct MapKeyOfT
{const K& operator()(const pair<K, V>& kv){return kv.first;}
};
struct SetKeyOfT
{const K& operator()(const K& key){return key;}
};

注:这里为什么只是返回key的数值,而不是在仿函数里面进行比较大小呢?

因为我们调用find的函数的时候不是传入两个数值进行比较的,而只是传一个。

使用红黑树封装的find

Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return cur;}}return nullptr;}

四、set和map的迭代器

STL源码采用下图结构,多搞了一个头结点。迭代器begin()可以指向header的左,迭代器end()指向header。

在这里插入图片描述

而小编使用的是无头结点进行封装的map和set,将nullptr作为结束

1、红黑树的begin()和end()

iterator begin()
{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return iterator(leftMin);
}iterator end()
{return iterator(nullptr);
}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);
}

封装了const是为了适配const的map和set

2、operator++

1、如果_node的右不为空,找右孩子的最左节点

2、如果_node的右为空,如果孩子是父亲的左就返回父亲,否则就继续向上遍历,如果走到nullptr那就是遍历完成

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

3、operator–

1、如果_node的左不为空,找左孩子的最右节点

2、如果_node的左为空,如果孩子是父亲的右就返回父亲,否则就继续向上遍历,如果走到nullptr那就是遍历完成

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

五、set的迭代器

对于set和map,它们的key都是不能改的。set的value不能修改,map的value可以修改。

因为set的value是不能改的,所以它的底层将普通迭代器和const迭代器全部封装成const迭代器来“解决”:

//注意这里是const_iterator变为iterator,在插入的时候会出现问题
typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator const_iterator;

这里iterator使用了const 的迭代器后,begin()和end()后面都需要加上const来解决问题;

iterator begin()const
{return _t.begin();
}
iterator end()const
{return _t.end();
}

这时使用迭代器调用上方函数会发现红黑树返回了普通迭代器类型的迭代器,类型不匹配。

在红黑树中补齐const版本的迭代器函数解决:

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);
}

六、map的迭代器

map的value是可以改的,所以需要分别设计普通迭代器和const迭代器

typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin()
{return _t.begin();
}iterator end()
{return _t.end();
}const_iterator begin() const
{return _t.begin();
}

七、迭代器的拷贝构造

STL库中的普通迭代器都可以转换为const迭代器,这是迭代器类的拷贝构造所支持的。

这个拷贝构造有点特殊:

struct __TreeIterator
{typedef RedBlackTreeNode<T> Node;Node* _node;typedef __TreeIterator<T,Ref,Ptr> Self;typedef __TreeIterator<T, T&, T*> iterator;__TreeIterator(const iterator& it):_node(it._node){}__TreeIterator(Node* node):_node(node){}
}

1、当这个模板的的Ref和PTR被实例化为T&和T*时,__RBTreeIterator(const iterator& it)就是一个拷贝构造(没啥意义)

2、当这个模板的的Ref和PTR被实例化为const T&和const T*时,__RBTreeIterator(const iterator& it)就是一个构造函数,

支持用普通迭代器去构造const迭代器。此时const迭代器的拷贝构造函数则由编译器自动生成,刚好满足迭代器值拷贝的特点。

八、源码

1、set.h

#include "9.14RedBlackTree.h"namespace sqy
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator iterator;//注意这里是const_iterator变为iterator,在插入的时候会出现问题typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator const_iterator;pair<iterator,bool> insert(const K& key){pair<typename RedBlackTree<K, K, SetKeyOfT>::iterator,bool> ret = _t.Insert(key);//这里调用insert返回的是普通迭代器return pair<iterator, bool>(ret.first, ret.second);//这里需要用普通迭代器拷贝构造const的迭代器}iterator begin()const {return _t.begin();}iterator end() const{return _t.end();}private:RedBlackTree<K, K, SetKeyOfT> _t;};
}

2、map.h

#include "9.14RedBlackTree.h"
namespace sqy
{template<class K,class V>class map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin() const{return _t.begin();}V& operator[](const K& key){pair<iterator, bool>ret = _t.Insert(make_pair(key, V()));return ret.first->second;}const_iterator end()const{return _t.end();}pair<iterator,bool> insert(const pair<K,V>& kv){return _t.Insert(kv);}private:RedBlackTree<K, pair<const K,V>, MapKeyOfT> _t;};
}

3、RedBlackTree.h

#include <iostream>
#include <cassert>
using namespace std;enum Color
{RED,BLACK
};template<class T>
struct RedBlackTreeNode
{T _data;RedBlackTreeNode<T>* _left;//该节点的左孩子RedBlackTreeNode<T>* _right;//该节点的右孩子RedBlackTreeNode<T>* _parent;//该节点是父亲节点Color _col;RedBlackTreeNode(const T& data): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}
};template<class T,class Ref,class Ptr>
struct __TreeIterator
{typedef RedBlackTreeNode<T> Node;Node* _node;typedef __TreeIterator<T,Ref,Ptr> Self;typedef __TreeIterator<T, T&, T*> iterator;__TreeIterator(const iterator& it):_node(it._node){}__TreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right)//parent不为空{cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node->_left){Node* subright = _node->_left;while (subright->_right){subright = subright->_right;}_node = subright;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator!=(const Self& s)const{return _node != s._node;}bool operator==(const Self& s)const{return _node == s._node;}
};template<class K, class T, class KeyOfT>
class RedBlackTree
{typedef RedBlackTreeNode<T> Node;
public:typedef __TreeIterator<T, T&, T*> iterator;typedef __TreeIterator<T, const T&, const T*> const_iterator;iterator begin(){Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return iterator(leftMin);}iterator end(){return iterator(nullptr);}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);}Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return cur;}}return 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* cur = _root;Node* parent = nullptr;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(cur), false);}}cur = new Node(data);Node* newnode = cur;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// ... 控制平衡while (parent && parent->_col == RED)//parent不为空并且为红进循环{Node* grandfather = parent->_parent;if (grandfather->_left == parent){if (parent->_left == cur){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;}}else{Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//叔叔存在并且叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}else{if (parent->_right == cur){Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);}bool IsBalance(){return _IsBalance(_root);}
private:bool checkColour(Node* root, int blacknum, int beachmark){if (root == nullptr){if (blacknum != beachmark){return false;}return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return checkColour(root->_left, blacknum, beachmark) && checkColour(root->_right, blacknum, beachmark);}bool _IsBalance(Node* root){if (root == nullptr){return true;}if (root->_col != BLACK){return false;}//基准值int beanchmark = 0;Node* cur = root;while (cur){if (cur->_col == BLACK){++beanchmark;}cur = cur->_left;}return checkColour(root, 0, beanchmark);}void RotateR(Node* parent){Node* cur = parent->_left;Node* curRight = cur->_right;parent->_left = curRight;cur->_right = parent;Node* ppNode = parent->_parent;if (curRight){curRight->_parent = parent;}parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = cur;}else{ppNode->_right = cur;}cur->_parent = ppNode;}}void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft)//判断是否为空,空的话就不用接上父亲节点{curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}
private:Node* _root = nullptr;
};

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

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

相关文章

Python Opencv实践 - 视频文件操作

参考资料&#xff1a; 视频处理VideoCapture类---OpenCV-Python开发指南&#xff08;38&#xff09;_python opencv videocapture_李元静的博客-CSDN博客 OpenCV VideoCapture.get()参数详解 - 简书FOURCC四字符码对照表_4fvcc_Kellybook的博客-CSDN博客 import cv2 as cv im…

02目标检测-传统检测方法

目录 一、目标学习的检测方法变迁及对比 二、 基于传统手工特征的检测算法的定义 三、传统主要手工特征与算法 Haar特征与 人脸检测算法 - Viola-Jones(了解) HOG特征与 SVM 算法(了解)&#xff08;行人检测、opencv实现&#xff09; SIFT特征与SIFT算法(了解) DPM&#…

Python中异常处理4-4

在Python中的异常处理4-1_棉猴的博客-CSDN博客中提到&#xff0c;在try块中的代码运行时如果出现异常&#xff0c;会自动抛出这个异常。可以通过raise语句手动抛出异常。 1 raise语句手动抛出异常 raise后面跟要抛出的异常类或者异常类的实例&#xff0c;表示手动抛出该异常&…

看好多人都在劝退学计算机,可是张雪峰又 推荐过计算机,所以计算机到底是什么样 的?

张雪峰高考四百多分&#xff0c;但是他现在就瞧不起400多分的学生。说难听点&#xff0c;六七百分的 热门专业随便报谁不会啊&#xff1f; 计算机专业全世界都是过剩的&#xff0c;今年桂林电子科技&#xff0c;以前还是华为的校招大学&#xff0c;今年 计算机2/3待业。这个世…

听GPT 讲Istio源代码--istioctl

在 Istio 项目的 istioctl 目录中&#xff0c;有一些子目录&#xff0c;每个目录都有不同的作用和功能。以下是这些子目录的详细介绍&#xff1a; /pkg: pkg 目录包含了 istioctl 工具的核心代码和库。这些代码和库提供了与 Istio 控制平面交互的功能&#xff0c;例如获取和修改…

java:逆序排序的三种方法

// 逆序第一种方法 public static void main(String[] args) {int arr[] {11, 22, 33, 44, 55, 66};for (int i arr.length-1; i > 0; i--) {System.out.print("\t"arr[i]);}}缺点&#xff1a;这个是直接逆转&#xff0c;如果里面是随机数没办法比较 逆序第二种…

PostGreSQL:时间戳时区问题

时间|日期类型 PostGreSQL数据库内置的时间类型如下&#xff0c;注意到&#xff1a;内置的时间类型被分为了with time zone-带时区、without time zone-不带时区两种类型&#xff0c; time、timestamp和interval都可以接受一个可选的精度值 p&#xff08;取值&#xff1a;0-6&a…

ChatGLM2-6B Lora 微调训练医疗问答任务

一、ChatGLM2-6B Lora 微调 LoRA 微调技术的思想很简单&#xff0c;在原始 PLM (Pre-trained Language Model) 增加一个旁路&#xff0c;一般是在 transformer 层&#xff0c;做一个降维再升维的操作&#xff0c;模型的输入输出维度不变&#xff0c;来模拟 intrinsic rank&…

pytorch代码实现之动态卷积模块ODConv

ODConv动态卷积模块 ODConv可以视作CondConv的延续&#xff0c;将CondConv中一个维度上的动态特性进行了扩展&#xff0c;同时了考虑了空域、输入通道、输出通道等维度上的动态性&#xff0c;故称之为全维度动态卷积。ODConv通过并行策略采用多维注意力机制沿核空间的四个维度…

【JavaSE笔记】抽象类与接口

一、抽象类 1、概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就是抽象类。 package demo2…

气传导耳机哪个好?值得推荐的气传导耳机分享

​随着生活节奏的加快&#xff0c;人们越来越关注听力健康。气传导耳机以其独特的传导方式和舒适的佩戴感受&#xff0c;逐渐成为耳机市场的新宠。气传导耳机不入耳设计听音&#xff0c;让你在享受音乐的同时&#xff0c;也能保护你的听力安全。今天我们就一起来看看几款值得大…

饲料添加剂 微生物 屎肠球菌

声明 本文是学习GB 7300.503-2023 饲料添加剂 第5部分&#xff1a;微生物 屎肠球菌. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了饲料添加剂屎肠球菌的技术要求、采样、检验规则、标签、包装、运输、贮存和保质 期&#xff0…