1. 调整之前实现的红黑树的insert
1.1 整体框架的搭建
新建两个头文件,Mymap.h和Myset.h,一个源文件test.cpp,然后把之前实现的红黑树拷贝一份过来。
为了和库里面的一些东西区分开,我们还是把所有自己实现的内容都放在自己的命名空间里。
在Mymap.h中搭建框架。key参数就⽤K,value参数就⽤V。
代码语言:javascript
AI代码解释
#include "RBTree.h" namespace lyj { template<class K, class V> class map { private: RBTree<K, pair<K, V>> _t; }; }在Myset.h中搭建框架。
代码语言:javascript
AI代码解释
#include "RBTree.h" namespace lyj { template<class K> class set { private: RBTree<K, K> _t; }; }直接对这个RBTree.h进行修改。
首先就是把模板参数改掉,红⿊树中的数据类型我们使⽤T。
代码语言:javascript
AI代码解释
template <class T> struct RBTreeNode { T _data; RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; Colour _col; RBTreeNode(const T& data) :_data(data) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) {} };key参数就⽤K,这里的T就是决定到底是set还是map的。
如果我们传一个K过去就是set,传pair过去就是map。
1.2 insert的修改
因为我们把红⿊树中的数据类型用T来表示了,也就是_data,这个_data是一个泛型,可能是set的K,可能是map的pair,用以前的逻辑就不能满足这个比较。
这个_kv换成_data也没用,不适用于set的K场景,并且pair自身支持的比较方法也不是我们想要的,我们需要任何时候都只比较K。
此时我们就需要实现一个仿函数。就是取出K来,set中就取K,map中就取first。
在Myset.h中set类里实现仿函数Set_Key_Of_T。
代码语言:javascript
AI代码解释
struct Set_Key_Of_T { const K& operator()(const K& key) { return key; } };set里这个仿函数就是给 key直接返回key就行。
在Mymap.h中map类里实现仿函数Map_Key_Of_T。
代码语言:javascript
AI代码解释
struct Map_Key_Of_T { const K& operator()(const pair<K, V>& kv) { return kv.first; } };map的仿函数就是返回pair里的first。
有仿函数之后,红黑树的模板参数也要多加一个了,叫KeyOfT。
代码语言:javascript
AI代码解释
template<class K, class T, class KeyOfT> class RBTree { //... }insert里的比较逻辑就要要成用这个仿函数写的逻辑。
map的find返回的是一个pair,这个pair的first是一个迭代器,second是一个bool值,所以这里的返回值也要修改一下。
代码语言:javascript
AI代码解释
pair<Iterator, bool> insert(const T& data) { }插入成功,就返回新插入的值的迭代器和true,插入失败就返回已经存在的这个值的迭代器和false。所以整体代码如下。
代码语言:javascript
AI代码解释
pair<Iterator, bool> insert(const T& data) { if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK; //根节点为黑色 return make_pair(Iterator(_root, _root), true); } Node* parent = nullptr; Node* cur = _root; KeyOfT com; //仿函数 while (cur) { if (com(cur->_data) > com(data)) { parent = cur; cur = cur->_left; } else if (com(cur->_data) < com(data)) { parent = cur; cur = cur->_right; } else return make_pair(Iterator(cur, _root), false); } cur = new Node(data); Node* newnode = cur; //记录新插入的节点 cur->_col = RED; //新插入节点为红色 if (com(cur->_data) < com(parent->_data)) { parent->_left = cur; } else { parent->_right = cur; } cur->_parent = parent; while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; if (parent == grandfather->_left) //u在右 { Node* uncle = grandfather->_right; if (uncle && uncle->_col == RED) //u存在且为红 { parent->_col = BLACK; //u和p变黑 uncle->_col = BLACK; grandfather->_col = RED;//g变红 cur = grandfather; //继续向上更新 parent = cur->_parent; } else //u不存在 或 u存在且为黑 { if (cur == parent->_left) //单旋 { rotateR(grandfather);//以g为旋转点右旋 parent->_col = BLACK; //变色 grandfather->_col = RED; } else //双旋 { rotateL(parent); //先对p左旋 rotateR(grandfather);//再对g右旋 //变色 cur->_col = BLACK; grandfather->_col = RED; } break; } } else //u在左 { Node* uncle = grandfather->_left; if (uncle && uncle->_col == RED) //u存在且为红 { parent->_col = BLACK; //p和u变黑 uncle->_col = BLACK; grandfather->_col = RED;//g变红 cur = grandfather; //继续向上更新 parent = cur->_parent; } else //u不存在 或 存在且为黑 { if (cur == parent->_right) //单旋 { rotateL(grandfather);//以g为中心左旋 parent->_col = BLACK; //p变黑 grandfather->_col = RED;//g变红 } else //双旋 { rotateR(parent);//先以p为中心右旋 rotateL(grandfather);//再以g为中心左旋 cur->_col = BLACK; //c变黑 grandfather->_col = RED;//g变红 } break; } } } _root->_col = BLACK; return make_pair(Iterator(newnode, _root), true); }上面的代码相比之前实现的insert,只有插入部分的代码修改了,旋转部分没做修改。
然后我们用set的insert测试一下。
在Myset.h中加上insert的函数,在set类中public实现。
代码语言:javascript
AI代码解释
public: pair<iterator, bool> insert(const K& key) //插入 { return _t.insert(key); } private: RBTree<K, K, Set_Key_Of_T> _t;在Mymap.h中加上insert的函数,在map类中public实现。
代码语言:javascript
AI代码解释
public: pair<iterator, bool> insert(const pair<K, V>& kv) //插入 { return _t.insert(kv); } private: RBTree<K, pair<K, V>, Map_Key_Of_T> _t;在test.cpp中测试。如果没报错,目前这个insert就是对的。
代码语言:javascript
AI代码解释
#include "Myset.h" #include "Mymap.h" int main() { lyj::set<int> s; //用自己实现的set s.insert(5); s.insert(3); s.insert(2); s.insert(4); s.insert(1); return 0; }2. iterator 迭代器的实现
2.1 部分运算符重载
这里需要同时考虑普通的迭代器和const迭代器,所以还是要一个类模板来实现。
代码语言:javascript
AI代码解释
template<class T, class ref, class ptr> struct RBTree_Iterator { typedef RBTreeNode<T> Node; typedef RBTree_Iterator<T, ref, ptr> Self; };还需要一个节点的指针,并写一个构造函数。
代码语言:javascript
AI代码解释
template<class T, class ref, class ptr> struct RBTree_Iterator { typedef RBTreeNode<T> Node; typedef RBTree_Iterator<T, ref, ptr> Self; Node* _node; RBTree_Iterator(Node* node) :_node(node) {} };然后还是在类里实现一下operator*, operator->, operator!= 和 operator== 这4个运算符重载。
代码语言:javascript
AI代码解释
ref operator*() { return _node->_data; } ptr operator->() { return &_node->_data; } bool operator!=(const Self& s) const { return _node != s._node; } bool operator==(const Self& s) const { return _node == s._node; }然后在RBTree类里在重命名一下。
代码语言:javascript
AI代码解释
typedef typename RBTree_Iterator<T, T&, T*> Iterator; typedef typename RBTree_Iterator<T, const T&, const T*> const_Iterator;2.2 迭代器
2.2.1 begin和end
以下图为例,map和set的迭代器⾛的是中序遍历,左⼦树->根结点->右⼦树,那么begin()会返回中序第⼀个结点的iterator也就是10 所在结点的迭代器。
end()如何表⽰呢?图中,当it指向50时,++it时,50是40的右,40是30的右,30是18的右,18
到根没有⽗亲,没有找到孩⼦是⽗亲左的那个祖先,这是⽗亲为空了,那我们就把it中的结点指针
置为nullptr,我们⽤nullptr去充当end。
在RBTree.h的RBTree类中public实现。
代码语言:javascript
AI代码解释
Iterator Begin() { Node* cur = _root; while (cur && cur->_left) { cur = cur->_left; //找最左节点 } return Iterator(cur); } const_Iterator Begin() const //const迭代器 { Node* cur = _root; while (cur && cur->_left) { cur = cur->_left; //找最左节点 } return Iterator(cur); }代码语言:javascript
AI代码解释
Iterator End() { return Iterator(nullptr); } const_Iterator End() const //const迭代器 { return Iterator(nullptr); }在Myset.h中set类里要封装一下。
代码语言:javascript
AI代码解释
public: typedef typename RBTree<K, K, Set_Key_Of_T>::Iterator iterator; typedef typename RBTree<K, K, Set_Key_Of_T>::const_Iterator const_iterator; iterator begin() { return _t.Begin(); } iterator end() { return _t.End(); } const_iterator begin() const //const迭代器 { return _t.Begin(); } const_iterator end() const { return _t.End(); }在Mymap.h中map类里也要封装一下。
代码语言:javascript
AI代码解释
public: typedef typename RBTree<K, pair<K, V>, Map_Key_Of_T>::Iterator iterator; typedef typename RBTree<K, pair<K, V>, Map_Key_Of_T>::const_Iterator const_iterator; iterator begin() { return _t.Begin(); } iterator end() { return _t.End(); } const_iterator begin() const //const迭代器 { return _t.Begin(); } const_iterator end() const { return _t.End(); }2.2.2 ++和--
迭代器++的核⼼逻辑就是不看全局,只看局部,只考虑当前中序局部要访问的下⼀个结点。
- 迭代器++时,如果it指向的结点的右⼦树不为空,代表当前结点已经访问完了,要访问下⼀个结点是右⼦树的中序第⼀个,⼀棵树中序第⼀个是最左结点,所以直接找右⼦树的最左结点即可。
- 迭代器++时,如果it指向的结点的右⼦树空,代表当前结点已经访问完了且当前结点所在的⼦树也访问完了,要访问的下⼀个结点在当前结点的祖先⾥⾯,所以要沿着当前结点到根的祖先路径向上找。
如果当前结点是⽗亲的左,根据中序左⼦树->根结点->右⼦树,那么下⼀个访问的结点就是当前结点的⽗亲。
如下图:it指向25,25右为空,25是30的左,所以下⼀个访问的结点就是30。
www.dongchedi.com/article/7598662619591098905
www.dongchedi.com/article/7598661663487148606
www.dongchedi.com/article/7598661165073383960
www.dongchedi.com/article/7598661590610706968
www.dongchedi.com/article/7598659598723203609
www.dongchedi.com/article/7598660331644518974
www.dongchedi.com/article/7598659182870970942
www.dongchedi.com/article/7598659511842849304
www.dongchedi.com/article/7598658577318265369
www.dongchedi.com/article/7598660363760206398
www.dongchedi.com/article/7598659848598962750
www.dongchedi.com/article/7598658231610884632
www.dongchedi.com/article/7598655027276071449
www.dongchedi.com/article/7598656521844949566
www.dongchedi.com/article/7598656663327261246
www.dongchedi.com/article/7598657570517058110
www.dongchedi.com/article/7598656249982550590
www.dongchedi.com/article/7598655008087196222
www.dongchedi.com/article/7598654082278408728
www.dongchedi.com/article/7598654850569667096
www.dongchedi.com/article/7598653079990911513
www.dongchedi.com/article/7598653503171052094
www.dongchedi.com/article/7598653873355948569
www.dongchedi.com/article/7598651016690975257
www.dongchedi.com/article/7598653584766632510
www.dongchedi.com/article/7598650859257397785
www.dongchedi.com/article/7598650344322925081