当前位置: 首页 > news >正文

利用红黑树封装实现map,set

目录

1.通用红黑树实现

1.1 初步结构实现

1.2 利用多模版改变传入参数

1.3 通用红黑树内部数据比较问题

1.4 map,set初步结构代码

2.通用红黑树迭代器实现

2.1 基本迭代器结构实现

2.2 operator++实现

2.2 operator--实现

3. map实现[ ]

4. 通用红黑树完整代码

5. 利用红黑树set,map代码


1.通用红黑树实现

1.1 初步结构实现

要利用红黑树封装map,set,发现map的参数是pair类型,而set只有key,正常的话可能会要实现两个红黑树分别对着两个结构进行封装,但是这两个红黑树的大部分接口都类似,此时就可以利用模版,根据传进去的参数,编译器进行实例化

// 枚举值表⽰颜⾊
enum Colour
{RED,BLACK
};
// 通用模版
template<class T>
struct RBTreeNode
{T _kv;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};
template<class T>
class RBTree
{typedef RBTreeNode<T> Node;
public://接口实现
private:Node* _root = nullptr;
};

1.2 利用多模版改变传入参数

对于set,find/erase/insert时的函数参数都是Key,因此一个模版就可以,但是对于map,虽然find/erase/时的函数参数也是一样的,但是insert函数时插入的数据时pair类型,因此只有一个模版参数是无法使用的,此时就可以多使用一个模版参数就可以区分,对于set来说K,T两个模版参数都是一样的,对于map,K存储pair类型的first数据,T存储pair类型
//set,一个模版参数就可以
Node* Find(const K& key)
bool Insert(const K& key)
Node* erase(const K& key)
//map,需要两个模版参数,K存储pair类型的first数据,T存储pair类型
Node* Find(const K& key)
bool Insert(const T& kv)
Node* erase(const K& key)
template<class K,class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:
//利用不同模版参数,控制不同形参传入bool Insert(const T& kv)Node* Find(const K& key)private:Node* _root = nullptr;
};

1.3 通用红黑树内部数据比较问题

在上文,对于不同参数的传入,利用了模版解决,这时又有新的问题,来看看下面关于find的代码

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

发现这里cur内的参数是pair,利用pair的first数据比较,这里对于map来说没有问题,但是对于set就有问题,因为set没有first,这是就可以利用仿函数解决,对红黑树加入一个仿函数模版比较

通用find代码实现

template<class K,class T, class KeyOfT>//KeyOfT仿函数模版
class RBTree
{typedef RBTreeNode<T> Node;
public:bool Insert(const T& kv)Node* Find(const K& key)
{Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_kv) < kot(kv)){cur = cur->_right;}else if (kot(cur->_kv) > kot(kv)){cur = cur->_left;}else{return cur;}}return nullptr;
}
private:Node* _root = nullptr;
};

1.4 map,set初步结构代码

map.h

namespace chuxin
{template<class K, class V>class map{struct MapKeyOfT{//利用仿函数const K& operator()(const pair<K, V>& kv){return kv.first;}};public:private:RBTree<K, pair< K, V>, MapKeyOfT> _t;};
}

set.h

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

2.通用红黑树迭代器实现

2.1 基本迭代器结构实现

这里的迭代器基本结构和list迭代器类似,都是用指针封装迭代器,如果不清楚list迭代器失现,请移步list类的常用接口实现及迭代器,这里就不在叙述,同样由于红黑树的迭代器支持 ->,因此也需要多个参数模版实现

template<class T>
struct RBTreeNode
{T _kv;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};
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->_kv;}Ptr operator->(){return &_node->_kv;}bool operator!= (const Self& s) const{return _node != s._node;}bool operator== (const Self& s) const{return _node == s._node;}
};
 set的iterator也不⽀持修改,我们把set的第⼆个模板参数改成const K即可,
RBTree<K,const K, SetKeyOfT> _t
map的iterator不⽀持修改key但是可以修改value,我们把map的第⼆个模板参数pair的第⼀个参
数改成const K即可
RBTree<K, pair<const K, V>, MapKeyOfT> _t

2.2 operator++实现

map和set的迭代器⾛的是中序遍历,左⼦树->根结点->右⼦树,那么begin()会返回中序第⼀个结点(左子树的最左节点)的iterator也就是10所在结点的迭代器。
对于++有以下两个场景
迭代器++时,如果it指向的结点的右⼦树不为空,代表当前结点已经访问完了,要访问下⼀个结点是右⼦树的中序第⼀个,⼀棵树中序第⼀个是最左结点,所以直接找右⼦树的最左结点即可。
Self operator++()
{// 右不为空,中序下一个访问的节点是右子树的最左(最小)节点if (_node->_right){Node* min = _node->_right;while (min->_left){min = min->_left;}_node = min;}// 右为空else{}
}
迭代器++时,如果it指向的结点的右⼦树空,代表当前结点已经访问完了且当前结点所在的⼦树也访问完了,要访问的下⼀个结点在当前结点的祖先⾥⾯,所以要沿着当前结点到根的祖先路径向上找。
	Self operator++(){// 右不为空,中序下一个访问的节点是右子树的最左(最小)节点if (_node->_right){Node* min = _node->_right;while (min->_left){min = min->_left;}_node = min;}// 右为空else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}}

2.2 operator--实现

迭代器--的实现跟++的思路完全类似,逻辑正好反过来即可,因为他访问顺序是右⼦树->根结点->
左⼦树。
end()如何表⽰呢?如下图:当it指向50时,++it时,50是40的右,40是30的右,30是18的右,18
到根没有⽗亲,没有找到孩⼦是⽗亲左的那个祖先,这是⽗亲为空了,那我们就把it中的结点指针
置为nullptr,我们⽤nullptr去充当end。
对于--end(),特殊处理,走到中序最后一个结点,整棵树的最右结点,因此需要_root节点
template<class T, class Ref, class Ptr>//利用指针封装迭代器
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;Node* _root;RBTreeIterator(Node* node,Node* root):_node(node),_root(root){}
Self operator--()
{if (_node == nullptr)  // --end(){// --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else if (_node->_left){// 左子树不为空,中序左子树最后一个Node* rightMost = _node->_left;while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else{// 孩子是父亲右的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}

3. map实现[ ]

map要⽀持[ ]主要需要修改insert返回值⽀持,修改RBtree中的insert返回值为
pair<Iterator, bool> Insert(const T& data)
	V& operator[](const K& key){pair<iterator, bool> ret = insert({ key, V() });return ret.first->second;}

4. 通用红黑树完整代码

#include<iostream>
#include<assert.h>
using namespace std;
// 枚举值表⽰颜⾊
enum Colour
{RED,BLACK
};
// 通用模版
template<class T>
struct RBTreeNode
{T _kv;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};
template<class T, class Ref, class Ptr>//利用指针封装迭代器
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;Node* _root;RBTreeIterator(Node* node,Node* root):_node(node),_root(root){}Ref operator*(){return _node->_kv;}Ptr operator->(){return &_node->_kv;}bool operator!= (const Self& s) const{return _node != s._node;}bool operator== (const Self& s) const{return _node == s._node;}Self operator++(){// 右不为空,中序下一个访问的节点是右子树的最左(最小)节点if (_node->_right){Node* min = _node->_right;while (min->_left){min = min->_left;}_node = min;}// 右为空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 == nullptr)  // --end(){// --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else if (_node->_left){// 左子树不为空,中序左子树最后一个Node* rightMost = _node->_left;while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else{// 孩子是父亲右的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}
};
template<class K,class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:// 旋转代码的实现跟AVL树是⼀样的,只是不需要更新平衡因⼦typedef RBTreeIterator<T, T&, T*> Iterator;typedef RBTreeIterator<T, const T&, const T*> ConstIterator;Iterator Begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Iterator End(){return Iterator(nullptr, _root);}ConstIterator Begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return ConstIterator(cur, _root);}ConstIterator End() const{return ConstIterator(nullptr, _root);}pair<Iterator, bool> Insert(const T& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return {Iterator(_root,_root),true};}Node* parent = nullptr;Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_kv) < kot(kv)){parent = cur;cur = cur->_right;}else if (kot(cur->_kv) > kot(kv)){parent = cur;cur = cur->_left;}else{return { Iterator(cur,_root),false };}}cur = new Node(kv);// 新增结点。颜⾊红⾊给红⾊Node* newnode = cur;cur->_col = RED;if (kot(parent->_kv) < kot(kv)){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;// u存在且为红 -》变⾊再继续往上处理if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{//u不存在或存在且为⿊if (cur == parent->_left){//右单旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//左右双旋RotateL(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{//u不存在或存在且为⿊if (cur == parent->_right){//左单旋RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//右左双旋RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return { Iterator(newnode,_root),true };}void RotateR(Node* parent)//右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;subL->_right = parent;parent->_left = subLR;Node* parendP = parent->_parent;if (subLR)subLR->_parent = parent;parent->_parent = subL;if (parendP == nullptr){_root = subL;subL->_parent = nullptr;}else//当旋转的是某一颗树的子树{if (parent == parendP->_left){parendP->_left = subL;}else{parendP->_right = subL;}subL->_parent = parendP;}}void RotateL(Node* parent)//左单旋{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_kv) < kot(key)){cur = cur->_right;}else if (kot(cur->_kv) > kot(key)){cur = cur->_left;}else{return cur;}}return nullptr;}void InOrder(){_InOrder(_root);cout << endl;}int Size(){return _Size(_root);}
private:int _Size(Node* root){if (root == nullptr)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 << ":" << root->_kv.second << endl;_InOrder(root->_right);}private:Node* _root = nullptr;
};

5. 利用红黑树set,map代码

map
namespace chuxin
{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>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end()  const{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({ key, V() });return ret.first->second;}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}

set

#include"RBtree.h"
namespace chuxin
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end()  const{return _t.End();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, const K, SetKeyOfT> _t;};
}

http://www.dtcms.com/a/294024.html

相关文章:

  • Keil MDK5 介绍与安装教程
  • Python Day22 - 复习日
  • 【bug】Jetson Orin NX apt更换国内源
  • #Linux权限管理:从“Permission denied“到系统安全大师
  • 如何使用 minio 完成OceanBase社区版的归档和备份
  • [Bug | Cursor] import error: No module named ‘data‘
  • SpringCloud sentinel服务熔断 服务降级
  • 一个没有手动加分号引发的bug
  • python---元组(Tuple)
  • C#简介(从入门到精通)
  • 判断矩形能否放入多边形内——cad c# 二次开发实现
  • 【服务器】 MCTP Over PCIe 的内容、用途、工作原理及硬件设计注意事项
  • 数据结构(2)顺序表算法题
  • C#使用socket报错 System.Net.Sockets.SocketException:“在其上下文中,该请求的地址无效。
  • .net平台的跨平台桌面应用开发的技术方案总结对比
  • 【黑马SpringCloud微服务开发与实战】(六)分布式事务
  • Matlab学习笔记:逻辑基础
  • PyTorch武侠演义 第一卷:初入江湖 第6章:驿站的秘密信鸽
  • Apache JMeter 使用记录踩坑
  • 前端模块化:CommonJS 与 ES Module
  • 性能测试-jmeter实战5
  • 4️⃣字典(dict)速查表
  • I2C控制器
  • 传统RNN模型笔记:输入数据长度变化的结构解析
  • 通用图片 OCR 到 Word API 数据接口
  • 数据结构自学Day13 -- 快速排序--“前后指针法”
  • 显微科研中的关键选择:不同显微镜相机技术特性与应用适配性全面解析
  • SpringCloudGateWay 使用nacos网关自动负载均衡
  • nrm指南
  • Sklearn 机器学习 线性回归