C++:红黑树封装实现map、set

一、map、set的底层结构

前面对map、set等树形结构的关联式容器进行了简单的介绍,了解到map、set都是由红黑树封装实现的。红黑树是一种由二叉搜索树进行平衡处理后的平衡树,其查找、插入、删除等操作的时间复杂度为O(logn),详情请参考数据结构——红黑树详解-CSDN博客。

红黑树的类模板参数value

map、set底层都是使用红黑树进行封装,set内数据为key,只要传递K一个类模板参数;而map内数据为pair<key-value>键值对,需要传递K,V两个类模板参数给红黑树。那怎么解决这个问题呢?

 

观察上图,set的类模板参数列表中只有一个Key,在内部将Key取别名为value_type。map的类模板参数列表中有Key、T,则是将Key、T构造为键值对后起别名为value_type。都将value_type类型传给红黑树的模板类参数列表,即由传递的value_type决定红黑树节点是set的key还是map的key-value,实现红黑树的泛型编程。

红黑树的类模板参数KeyOfValue

由于红黑数内数据类型的不确定,在查找,插入,删除等操作中,需要先取key值,再进行比较。所以我们需要向红黑树中传递KeyOfValue仿函数来进行取出key值的操作。

struct mapkeyofT {const k& operator()(const pair<k, v>& kv) {return kv.first;}};
struct setkeyofT {const k& operator()(const k& key) {return key;}};

 

二、红黑树的迭代器实现

红黑树迭代器的本质就是红黑树的结点指针,要将红黑树中的元素升序遍历,我们采用中序遍历的方式来重载迭代器的++和--操作。

2.1 operator++与operator--

operator++(左子树、根、右子树)就是找到下一个只比当前元素大的元素。

如果当前节点右子树不为空,去找右子树中的最小值,即找到右子树的最左节点。

如果当前节点右子树为空,说明以当前节点为根节点的树中序遍历结束。依次向上查找直到祖先节点(parent)中左子树为cur的节点。

    //按照中序遍历的方式,++self& operator++() {//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else {//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中左子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {//cur依旧为parent的右子树,说明更新后的cur中序遍历也结束了cur = parent;parent = cur->_parent;}_node = parent;}return *this;}

operator--(右子树、根、左子树)就是找到下一个只比当前元素小的元素。

如果当前节点左子树不为空,去找左子树中的最大值,即找到左子树的最右节点。

如果当前节点左子树为空,说明以当前节点为根节点的树中序遍历结束。依次向上查找直到祖先节点(parent)中右子树为cur的节点。

//如果左子树不为空,那么比这个元素大的下一个节点就是左子树的最大元素//也就是左子树的最右节点self& operator--() {if (_node->_left) {Node* subright = _node->_left;while (subright->_right) {subright = subright->_right;}_node = subright;}else{//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中右子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur) {cur = parent;parent = parent->_parent;}_node = parent;}}

2.2 operator==与operator!=

bool operator !=(const self& s) {return _node != s._node;}bool operator==(const self& s) {return _node = s._node;}

 2.3 operator*与operator->

operator*()返回迭代器指向节点的数据,operator->()返回迭代器指向节点的数据的地址。

T& operator*() {return _node->_data;}T* operator->() {return &_node->_data;}

完整迭代器实现如下:

template<class T>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T> self;Node* _node;RBTreeIterator(Node* node):_node(node){}//运算符重载:那个对象调用符号,传那个对象的this指针T& operator*() {return _node->_data;}T* operator->() {return &_node->_data;}//按照中序遍历的方式,++self& operator++() {//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else {//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中左子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//如果左子树不为空,那么比这个元素大的下一个节点就是左子树的最大元素//也就是左子树的最右节点self& operator--() {if (_node->_left) {Node* subright = _node->_left;while (subright->_right) {subright = subright->_right;}_node = subright;}else{//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中右子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur) {cur = parent;parent = parent->_parent;}_node = parent;}}bool operator !=(const self& s) {return _node != s._node;}bool operator==(const self& s) {return _node = s._node;}
};

三、红黑树的更改

之前已经对红黑树模拟实现,现在需要对红黑树进行一些更改方便map、set的封装。

3.1 红黑树的迭代器的封装

typedef RBTreeIterator<T> iterator;

begin()与end()函数实现

begin()应该是一棵红黑树的最小节点,即最左节点;end()为nullptr。

iterator begin() {Node* subleft = _root;while (subleft && subleft->_left) {subleft = subleft->_left;}return iterator(subleft);}iterator end()
{return iterator(nullptr);
}

3.2 红黑树的查找、插入

在红黑树的查找、插入操作中,需要先实例化一个keyofT对象使用仿函数取出key值,再比较大小。另外,为了map的operator[],红黑树的插入函数返回值需要更改为<Iterator,bool>的键对值。

pair<iterator, bool> Insert(const T& data){if (_root == nullptr) {_root = new Node(data);_root->_col = BLACK;Node* newnode = _root;return make_pair(iterator(newnode), true);}keyofT kot;Node* parent = nullptr;Node* cur = _root;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); // 在cur位置插入红色节点Node* newnode = cur;if (kot(parent->_data) < kot(data)) {//连接cur节点到红黑树parent->_right = cur;}else {parent->_left = cur;}cur->_parent = parent;return make_pair(iterator(newnode), true);;}

上述为红黑树插入函数,忽略旋转、变色操作。

3.3 红黑树更改后代码实现

#define  _CRT_SECURE_NO_WARNINGS
#pragma once
#include<vector>
#include<iostream>
#include<time.h>
using namespace std;enum Colour {//枚举类型,红黑树颜色RED,BLACK
};template<class T>//T决定节点是key还是k-v类型
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};template<class T>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T> self;Node* _node;RBTreeIterator(Node* node):_node(node){}//运算符重载:那个对象调用符号,传那个对象的this指针T& operator*() {return _node->_data;}T* operator->() {return &_node->_data;}//按照中序遍历的方式,++self& operator++() {//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else {//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中左子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//如果左子树不为空,那么比这个元素大的下一个节点就是左子树的最大元素//也就是左子树的最右节点self& operator--() {if (_node->_left) {Node* subright = _node->_left;while (subright->_right) {subright = subright->_right;}_node = subright;}else{//右子树为空,(以当前节点为根节点的树中序遍历结束)向上查找//下一个节点为祖先节点(parent)中右子树为cur的节点Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur) {cur = parent;parent = parent->_parent;}_node = parent;}}bool operator !=(const self& s) {return _node != s._node;}bool operator==(const self& s) {return _node = s._node;}
};template<class k, class T, class keyofT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef RBTreeIterator<T> iterator;iterator begin() {Node* subleft = _root;while (subleft && subleft->_left) {subleft = subleft->_left;}return iterator(subleft);}iterator end(){return iterator(nullptr);}pair<iterator, bool> Insert(const T& data){if (_root == nullptr) {_root = new Node(data);_root->_col = BLACK;Node* newnode = _root;return make_pair(iterator(newnode), true);}keyofT kot;Node* parent = nullptr;Node* cur = _root;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); // 在cur位置插入红色节点Node* newnode = cur;if (kot(parent->_data) < kot(data)) {//连接cur节点到红黑树parent->_right = cur;}else {parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//1:当叔叔存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上处理cur = grandfather;parent = cur->_parent;}else{//2:叔叔不存在或者存在且为黑,由第一种情况调整而来//旋转+变色if (cur == parent->_left){//     g//   p    u// c//此时路径上黑色节点不相同,且有连续的红色节点,不满足最长路径是最短路径的两倍RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else {//     g//   p    u//     cRotateL(parent);RotateR(grandfather);cur->_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{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色//      g//   u     p//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else {//      g//   u     p//       cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);;}void RotateL(Node* parent)//左单旋{++rotateSize;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) {subRL->_parent = parent;}subR->_left = parent;Node* ppnode = parent->_parent;//记录parent的父节点parent->_parent = subR;if (parent == _root)//考虑parent是该树的根节点{_root = subR;subR->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void RotateR(Node* parent)//右单旋{++rotateSize;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) {subLR->_parent = parent;}subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}size_t Size(){return _Size(_root);}size_t _Size(Node* root){if (root == NULL)return 0;return _Size(root->_left)+ _Size(root->_right) + 1;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << endl;_InOrder(root->_right);}void InOrder(){_InOrder(_root);}int GetRotateSize(){return rotateSize;}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}int Height(){return _Height(_root);}Node* Find(const k& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return NULL;}bool IsBalance(){if (_root && _root->_col == RED)return false;int refBlackNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)refBlackNum++;cur = cur->_left;}return Check(_root, 0, refBlackNum);}bool Check(Node* cur, int blackNum, int refBlackNum){//传blackNum,每次走到黑色节点++,为nullptr回到上一层调用走右子树,此时blackNum还是上一层的值if (cur == nullptr) {if (refBlackNum != blackNum) {cout << "黑色节点的数量不相等" << endl;return false;}return true;}if (cur->_col == RED && cur->_parent->_col == RED) {cout << "存在连续的红色节点" << endl;return false;}if (cur->_col == BLACK)++blackNum;return Check(cur->_left, blackNum, refBlackNum) && Check(cur->_right, blackNum, refBlackNum);}
private:Node* _root = nullptr;int rotateSize = 0;};

四、map、set封装红黑树

4.1 set封装红黑树

set内部成员变量为实例化的红黑树对象,插入、查找等成员函数为调用红黑树对应函数。

template<class k>
class set
{struct setkeyofT {const k& operator()(const k& key) {return key;}};
public:typedef typename RBTree<k, k, setkeyofT>::iterator iterator;iterator begin() {return _t.begin();}iterator end() {return _t.end();}pair<iterator, bool> insert(const k& key){return _t.Insert(key);}private:RBTree<k, k, setkeyofT> _t;};

4.2 map封装红黑树

map内部成员变量为实例化的红黑树对象,插入、查找等成员函数为调用红黑树对应函数。

与set不同的是,map多了operator[]重载,它会在内部调用map的insert函数。

map的operator[]重载

v& operator[](const k& key){pair<iterator, bool> ret = Insert(make_pair(key, v()));return ret.first->second;//key对应节点迭代器指向的数据value}

insert函数会返回插入元素的迭代器位置和元素是否存在的bool值构成的pair对象,在operator[]函数中,会先让map对象调用insert函数插入键值k和value的默认值(为value类型的默认构造函数生成的临时对象)所构成的pair对象。

其中ret.first就是获取插入后元素的迭代器,ret.first->second获得key所对应的value值。

当key存在时,插入失败。获取key所在pair对象的迭代器,ret.first->second为key所对应的value。

当key不存在时,插入成功。获取新插入pair对象的迭代器,ret.first->second为默认value。

所以,map的operator[]能够实现查找,修改,插入三个功能。

map封装红黑树实现

template<class k, class v>
class map
{struct mapkeyofT {const k& operator()(const pair<k, v>& kv) {return kv.first;}};
public:typedef typename RBTree<k, pair<const k, v>, mapkeyofT>::iterator iterator;//typedef创建了存在类型的别名,而typename告诉编译器RBTree<k, pair<const k, v>, mapkeyofT>::iterator是一个类型而不是一个成员。iterator begin() {return _t.begin();}iterator end() {return _t.end();}pair<iterator, bool> insert(const pair<k, v>& kv){return _t.Insert(kv);}v& operator[](const k& key){pair<iterator, bool> ret = Insert(make_pair(key, v()));return ret.first->second;//key对应节点迭代器指向的数据value}private:RBTree<k, pair<const k, v>, mapkeyofT> _t;};

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

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

相关文章

nature| 肠道中藏着癌症免疫治疗的关键

在疾病发生过程中&#xff0c;人体共生菌发生了系统性的变化&#xff0c;起到了正向或负向作用&#xff0c;因此可以把共生菌看作人体活动的一个重要外部器官。 癌细胞通过释放特定信号&#xff0c;给免疫系统“踩刹车”&#xff0c;抑制免疫系统的识别和杀伤。这类免疫系统的…

【管理咨询宝藏49】AA银行上市发展提升分析报告

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏49】AA银行上市发展提升分析报告 【格式】PPT版本&#xff0c;可编辑 【关键词】战略规划、商业分析、管理咨询 【强烈推荐】这是一套市面上非常…

python 05文件的读写

import os f open("data/test.txt","w") #如果文件不存在就创建文件 f.write("11,12\n21,22\n") #写入两行 第一行是11&#xff0c;12 第二行 21&#xff0c;22 f.close() #打开文件后要关闭写入f open("data/test.txt","r…

vscode 安装vim插件配置ctrl + c/v功能

搜索Vim插件 插件介绍部分有提示操作 首先安装该插件&#xff0c;然后按照下述步骤设置ctrl相关的快捷键&#xff0c;以便于脱离im快捷键而愉快的敲代码。 1.在“设置”搜索框内搜索vim.handleKeys&#xff0c;选择 Edit in settings.json 2. 设置ctrl-c,ctrl-v等快捷键置为fa…

数据库系统概论(超详解!!!)第三节 关系数据库标准语言SQL(Ⅵ)

1.空值的处理 空值就是“不知道”或“不存在”或“无意义”的值。 一般有以下几种情况&#xff1a; 该属性应该有一个值&#xff0c;但目前不知道它的具体值 &#xff1b;该属性不应该有值 &#xff1b;由于某种原因不便于填写。 1.空值的产生 空值是一个很特殊的值&#x…

uniapp使用npm命令引入font-awesome图标库最新版本并解决APP和小程序不显示图标的问题

uniapp使用npm命令引入font-awesome图标库最新版本 图标库网址&#xff1a;https://fontawesome.com/search?qtools&or 命令行&#xff1a; 引入 npm i fortawesome/fontawesome-free 查看版本 npm list fortawesome在main.js文件中&#xff1a; import fortawesome/fo…

2024新版PHP在线客服系统多商户AI智能在线客服系统源码机器人自动回复即时通讯聊天系统源码PC+H5

搭建环境&#xff1a; 服务器 CPU 2核心 ↑ 运存 2G ↑ 宽带 5M ↑ 服务器操作系统 Linux Centos7.6-7.9 ↑ 运行环境&#xff1a; 宝塔面板 Nginx1.18- 1.22 PHP 7.1-7.3 MYSQL 5.6 -5.7 朵米客服系统是一款全功能的客户服务解决方案&#xff0c;提供多渠道支持…

《从零开始学架构》读书笔记(一)

目录 软件架构设计产生的历史背景 软件架构设计的目的 系统复杂度来源 追求高性能 一、单机高性能 二、集群的高性能 追求高可用 一、计算高可用 二、存储高可用 追求可扩展性 一、预测变化 二、应对变化 追求安全、低成本、规模 一、安全 二、低成本 三、规模…

Golang | Leetcode Golang题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; func threeSumClosest(nums []int, target int) int {sort.Ints(nums)var (n len(nums)best math.MaxInt32)// 根据差值的绝对值来更新答案update : func(cur int) {if abs(cur - target) < abs(best - target) {best cur}}// 枚举 a…

java数据结构与算法刷题-----LeetCode326. 3 的幂

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 试除法是否是最大的3的幂的约数 试除法 解题思路&#xff1a;时…

何为网络协议?一图知晓网络过程。

网络协议就是计算机之间沟通的语言 为了有效地交流&#xff0c;计算机之间需要一种共同的规则或协议&#xff0c; 就像我们和老外沟通之前&#xff0c;要先商量好用哪种语言&#xff0c; 要么大家都说中文&#xff0c;要么大家都说英语&#xff0c;这才能有效地沟通。 网络协…

python爬虫学习第十六天--------URLError和HTTPError、cookie登录、Handler处理器

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…