【C++】基于红黑树封装set和map

头像
🚀个人主页:@小羊
🚀所属专栏:C++
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述

目录

  • 前言
    • 一、更高维度的泛型
    • 二、模版参数
    • 三、比较逻辑的重写
    • 四、迭代器
      • 4.1 const迭代器
      • 4.2 ++重载
      • 4.3 - -重载
    • 五、完整代码


前言

前面我们介绍了set和map的底层细节,在上篇文章中也简单实现了红黑树,而我们知道set和map的底层就是依靠红黑树实现的,那么在本文我们将学习如何基于红黑树来封装出set和map。
本篇文章会带你深入理解C++的三大特性之一——封装。

封装屏蔽底层细节,提供统一访问方式。


一、更高维度的泛型

| 红黑树部分代码:

#pragma once#include <iostream>
using namespace std;enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;RBTreeNode(const pair<K, V>& kv, Colour col = RED):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(col){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:RBTree() = default;RBTree(const RBTree<K, V>& t){_root = _copy(t._root);}~RBTree(){_Destroy(_root);_root = nullptr;}RBTree<K, V>& operator=(RBTree<K, V> t){swap(_root, t._root);return *this;}void InOrder(){_InOrder(_root);cout << endl;}bool Find(const pair<K, V>& kv){Node* pcur = _root;while (pcur){if (kv.first < pcur->_kv.first){pcur = pcur->_left;}else if (kv.first > pcur->_kv.first){pcur = pcur->_right;}else{return true;}}return false;}bool Insert(const pair<K, V>& kv){Node* pcur = _root;Node* parent = nullptr;if (pcur == nullptr)//当前还没有节点{_root = new Node(kv);_root->_col = BLACK;//根节点必须是黑色的return true;}while (pcur){if (kv.first < pcur->_kv.first){parent = pcur;pcur = pcur->_left;}else if (kv.first > pcur->_kv.first){parent = pcur;pcur = pcur->_right;}else{return false;}}pcur = new Node(kv);if (kv.first < parent->_kv.first){parent->_left = pcur;}else{parent->_right = pcur;}pcur->_parent = parent;Node* grandfather = parent->_parent;Node* uncle = nullptr;//当父节点存在,且颜色为红,需要往上更新while (parent && parent->_col == RED){if (parent == grandfather->_left){uncle = grandfather->_right;}else{uncle = grandfather->_left;}if (uncle && uncle->_col == RED)//情况一:u存在且为红{parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;if (grandfather->_parent == nullptr){grandfather->_col = BLACK;return true;}pcur = grandfather;parent = pcur->_parent;grandfather = parent->_parent;}else //情况二:u存在且为黑 / 情况三:u不存在{if (parent == grandfather->_left && pcur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_left && pcur == parent->_right){RotateL(parent);RotateR(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && pcur == parent->_left){RotateR(parent);RotateL(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && pcur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}break;}}return true;}
private:void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}Node* parentparent = parent->_parent;parent->_parent = subL;if (parentparent == nullptr){_root = subL;}else{if (parent == parentparent->_left){parentparent->_left = subL;}else{parentparent->_right = subL;}}subL->_parent = parentparent;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;Node* parentparent = parent->_parent;parent->_parent = subR;if (parentparent == nullptr){_root = subR;}else{if (parent == parentparent->_left){parentparent->_left = subR;}else{parentparent->_right = subR;}}subR->_parent = parentparent;}Node* _copy(Node* root){if (root == nullptr){return nullptr;}Node* newnode = new Node(root->_kv);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}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;
};

前面我们实现的红黑树存储的数据是键值对pair<K, V>,现在又需要基于红黑树封装出set和map,而我们知道set中存的是K,map中存的是pair<K, V>,它们数据类型不一样,那我们要拷贝两份红黑树的代码来分别封装set和map吗,如果真是这样那也太挫了。

我们可以想办法只用一个红黑树封装set和map,像这种使用不同类型在同一套代码中操作的需求,我们很容易就想到了模版。值得一说的是,我们实现的红黑树已经是一个类模版,不过这个类模版还不是太灵活,因为它存储类型是K还是pair<K, V>是写死的,并不能够做到完全泛型。

说到这里相信我们都想到了一个解决办法:把用于封装set和map的红黑树存储数据的类型也设计成一个模版参数,在实例化具体的类时根据传过去的值来确定是存储K还是pair<K, V>,而站在set和map层面它们肯定知道自己的数据类型。

RBTree.h:

template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& data, Colour col = RED): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(col){}
};template<class K, class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:RBTree() = default;RBTree(const RBTree<K, T>& t){_root = _copy(t._root);}~RBTree(){_Destroy(_root);_root = nullptr;}RBTree<K, T>& operator=(RBTree<K, T> t){swap(_root, t._root);return *this;}//...

MySet.h:

namespace yjz
{template<class K>class set{public:private:RBTree<K, const K> _t;};
}

MyMap.h:

namespace yjz
{template<class K, class V>class map{public:private:RBTree<K, pair<const K, V>> _t;};
}

上面我们使红黑树节点模版类的模版参数仅用T来表示,当用set实例化红黑树类模版时,红黑树节点类模版参数T是K类型;当用map实例化红黑树类模版时,红黑树节点类模版参数T是pair<K, V>类型。这里相当于实现了一个更高一点维度的泛型。


二、模版参数

有同学可能注意到了,上面用红黑树封装set和map时传过去了两个参数。一个T类型不是就能确定是K还是pair<K, V>了嘛,为什么还要传过去一个K呢?而且像set传过去两个一模一样的K感觉很奇怪。其实这是为了满足红黑树一些接口的特点而不得不这样做。

bool Find(const K& key)
bool Insert(const T& data)

像上面的查找和插入操作,仅通过一个参数T还解决不了问题。对于插入操作如果是set就插入K,如果是map就插入pair<K, V>;但是对于查找操作不管是set还是map我们只能通过key查找,这是set和map的特点。 对于map来说是通过keyvalue,不能说骑驴找驴。

另外,这里的FindInsert函数的返回值也需要改一改,Find函数应该返回节点的迭代器,Insert函数应该根据插入成功或失败返回相应的pair<iterator, bool>

Iterator Find(const K& key)
{KeyOfT kot;Node* pcur = _root;while (pcur){if (key < kot(pcur->_data)){pcur = pcur->_left;}else if (key > kot(pcur->_data)){pcur = pcur->_right;}else{return Iterator(pcur, _root);}}return End();
}pair<Iterator, bool> Insert(const T& data)
{KeyOfT kot;Node* pcur = _root;Node* parent = nullptr;if (pcur == nullptr)//当前还没有节点{_root = new Node(data);_root->_col = BLACK;//根节点必须是黑色的return make_pair(Iterator(_root, _root), true);}while (pcur){if (kot(data) < kot(pcur->_data)){parent = pcur;pcur = pcur->_left;}else if (kot(data) > kot(pcur->_data)){parent = pcur;pcur = pcur->_right;}else{return make_pair(Iterator(pcur, _root), false);}}pcur = new Node(data);Node* newnode = pcur;//记录新节点指针,旋转可能会更新pcurif (kot(data) < kot(parent->_data)){parent->_left = pcur;}else{parent->_right = pcur;}pcur->_parent = parent;Node* grandfather = parent->_parent;Node* uncle = nullptr;//当父节点存在,且颜色为红,需要往上更新while (parent && parent->_col == RED){if (parent == grandfather->_left){uncle = grandfather->_right;}else{uncle = grandfather->_left;}if (uncle && uncle->_col == RED)//情况一:u存在且为红{parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;if (grandfather->_parent == nullptr){grandfather->_col = BLACK;return make_pair(Iterator(newnode, _root), true);}pcur = grandfather;parent = pcur->_parent;grandfather = parent->_parent;}else //情况二:u存在且为黑 / 情况三:u不存在{if (parent == grandfather->_left && pcur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_left && pcur == parent->_right){RotateL(parent);RotateR(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && pcur == parent->_left){RotateR(parent);RotateL(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && pcur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}break;}}return make_pair(Iterator(newnode, _root), true);
}

三、比较逻辑的重写

当我们修改了红黑树的模版参数,那它的底层实现也要做相应的修改。
例如,插入操作的部分代码:

//...while (pcur){if (data < pcur->data){parent = pcur;pcur = pcur->_left;}else if (data > pcur->data){parent = pcur;pcur = pcur->_right;}else{return false;}}
//...

同学们想一下,这里还能用data < pcur->data这种直接比较的方式吗?
当然是不可以的。如果是set还好说,keykey比较没问题;但是map就有问题了,因为我们知道pair<K, V>不是单纯的依据first比较的,来看下图中pair<K, V>的比较方式:

在这里插入图片描述

从上图我们可以看到second也加入了比较逻辑,这和我们期望的只依靠first比较是不符合的。
可能有同学想到了用仿函数,但是这里普通的仿函数还解决不了问题,因为仿函数的作用是让我们可以灵活的定制实际需求,而这里的问题是不能知道到底是K还是pair<K, V>

这里我们就要想,我们期望的是:如果是set,就用key比较key;如果是map,就用first比较first。而站在红黑树的角度它是不知道接下来要实例化它的到底是set还是map的,那谁知道呢?当然是set和map他们自己知道嘛。
所以我们可以在set和map实例化红黑树时前,用仿函数提前把keyfirst取出来,这样在红黑树中比较时,通过回调的方式就能拿到我们想要的值。

这里我们再来回顾一下之前学的回调函数:回调函数不是由该函数的实现方直接调用,而是在待定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

MySet.h:

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

MyMap.h:

namespace yjz
{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;};
}

RBTree.h:

bool Insert(const T& data)
{KeyOfT kot;Node* pcur = _root;Node* parent = nullptr;if (pcur == nullptr)//当前还没有节点{_root = new Node(data);_root->_col = BLACK;//根节点必须是黑色的return true;}while (pcur){if (kot(data) < kot(pcur->_data)){parent = pcur;pcur = pcur->_left;}else if (kot(data) > kot(pcur->_data)){parent = pcur;pcur = pcur->_right;}else{return false;}}pcur = new Node(data);//...

四、迭代器

在这里插入图片描述

set和map的迭代器都是双向迭代器,既可以通过++操作符前进到下一个元素,也可以通过- -操作符回退到前一个元素。因为set和map的底层红黑树是二叉树,不能完成简单的++和- -,所以需要重载运算符。
这里我们先实现一下简单的构造、beginend,以及*->==!=的重载,最后再探讨++和- -的重载如何实现。

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->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};

beginend分别返回红黑树最左边节点的迭代器和最右边节点下一个空节点的迭代器。这里我们就暂且让end返回nullptr。(C++标准库中是借助哨兵节点解决end的。)
在这里插入图片描述

Iterator Begin()
{Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return Iterator(leftMost, _root);
}Iterator End()
{return Iterator(nullptr, _root);
}

4.1 const迭代器

除了普通迭代器,还需要const迭代器,const迭代器相较于普通迭代器主要是*->的重载,为了复用普通迭代器,可以增加模版参数来提高泛型,这个在实现list迭代器中也有使用。
另外还有,set的key是不能修改的,map的key也不能修改,但value是可以修改的。既然如此,我们在pair<K, V>中K前面加上const,就能巧妙的解决这个问题。对于set也可以和map保持一致的做法。

MySet.h:

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();}private:RBTree<K, const K, SetKeyOfT> _t;
};

MyMap.h:

template<class K, class V>
struct map
{struct MapKeyOfT{const K& operator()(const pair<const 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();}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

RBTree.h:

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*> ConstIterator;Iterator Begin(){Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return Iterator(leftMost, _root);}Iterator End(){return Iterator(nullptr, _root);}ConstIterator Begin() const{Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return ConstIterator(leftMost, _root);}ConstIterator End() const{return ConstIterator(nullptr, _root);}RBTree() = default;RBTree(const RBTree<K, T, KeyOfT>& t){_root = _copy(t._root);}~RBTree(){_Destroy(_root);_root = nullptr;}//...

4.2 ++重载

最后到了相对麻烦一点的++和- -重载,我们知道红黑树的遍历走的是中序,中序遍历是左子树、根节点、右子树,那如果通过++走到中序的下一个节点呢?

在这里插入图片描述

it当前指向8这个节点,按照中序遍历++应该指向11这个节点,因为左、根、右,所以++我们应该找当前节点的右节点,那么这时候就有两种情况,即右节点为空和右节点不为空。

上图情况一是右节点不为空,当右节点不为空时中序遍历的下一个节点并不一定就是当前节点的右孩子,而是当前节点右子树的最左节点。
情况二是右节点为空,右节点为空说明当前节点所在子树遍历完了,那当前节点所在子树遍历完了中序遍历下一个节点就是它的父亲吗?并不一定。接下来我们应该判断当前节点是它父亲的左孩子还是右孩子,如果是它父亲的右孩子,则说明它父亲所在子树也遍历完了,需要继续往上更新再做判断;如果是它父亲的左孩子,则下一个节点就是它的父亲。

Self& operator++()
{//1.当前节点右子树不为空if (_node->_right){Node* leftMost = _node->_right;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}_node = leftMost;}else//2.当前节点右子树为空(当前节点所在子树访问完了){Node* pcur = _node;Node* parent = pcur->_parent;while (parent && pcur == parent->_right){pcur = parent;parent = pcur->_parent;}_node = parent;}return *this;
}

4.3 - -重载

在这里插入图片描述
重载- -运算符和++类似,就是反过来而已。
假设当前节点的迭代器it,- -应该得到的是它的左孩子的迭代器,那这个时候也有两种情况,左孩子为空和左孩子不为空。
上图情况一是左孩子不为空,那- -得到的应该是其左子树的最右节点的迭代器;情况二是左孩子为空,左孩子为空说明当前节点所在子树遍历完了,如果当前节点是其父亲的左孩子,那说明其父亲所在子树也遍历完了,需要向上更新;如果当前节点是其父亲的右孩子,则- -得到的就是它的父亲。

值得注意的是,前面我们实现的end返回的是nullptr,而end返回的迭代器- -是要得到二叉树最右节点的迭代器的,显然我们这里的- -并不能实现,那我们就对这种情况做特殊处理。
如果当前指针为空,我们就通过_root来找到红黑树的最右节点。(这也是最开始在迭代器增加_root成员的原因。)

Self& operator--()
{//当迭代器是end()时特殊处理if (_node == nullptr){Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}//1.左子树不为空,找左子树最右节点else if (_node->_left){Node* rightMost = _node->_left;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else//2.左子树为空,当前节点所在子树访问完了{Node* pcur = _node;Node* parent = pcur->_parent;while (parent && pcur == parent->_left){pcur = parent;parent = parent->_parent;}_node = parent;}return *this;
}

五、完整代码

RBTree.h:

#pragma once#include <iostream>
using namespace std;enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& data, Colour col = RED): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(col){}
};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++(){//1.当前节点右子树不为空if (_node->_right){Node* leftMost = _node->_right;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}_node = leftMost;}else//2.当前节点右子树为空(当前节点所在子树访问完了){Node* pcur = _node;Node* parent = pcur->_parent;while (parent && pcur == parent->_right){pcur = parent;parent = pcur->_parent;}_node = parent;}return *this;}Self& operator--(){//当迭代器是end()时特殊处理if (_node == nullptr){Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}//1.左子树不为空,找左子树最右节点else if (_node->_left){Node* rightMost = _node->_left;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else//2.左子树为空,当前节点所在子树访问完了{Node* pcur = _node;Node* parent = pcur->_parent;while (parent && pcur == parent->_left){pcur = parent;parent = parent->_parent;}_node = parent;}return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}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, T&, T*> Iterator;typedef RBTreeIterator<T, const T&, const T*> ConstIterator;RBTree() = default;RBTree(const RBTree<K, T, KeyOfT>& t){_root = _copy(t._root);}~RBTree(){_Destroy(_root);_root = nullptr;}RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t){swap(_root, t._root);return *this;}void InOrder(){_InOrder(_root);cout << endl;}Iterator Begin(){Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return Iterator(leftMost, _root);}Iterator End(){return Iterator(nullptr, _root);}ConstIterator Begin() const{Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return ConstIterator(leftMost, _root);}ConstIterator End() const{return ConstIterator(nullptr, _root);}Iterator Find(const K& key){KeyOfT kot;Node* pcur = _root;while (pcur){if (key < kot(pcur->_data)){pcur = pcur->_left;}else if (key > kot(pcur->_data)){pcur = pcur->_right;}else{return Iterator(pcur, _root);}}return End();}pair<Iterator, bool> Insert(const T& data){KeyOfT kot;Node* pcur = _root;Node* parent = nullptr;if (pcur == nullptr)//当前还没有节点{_root = new Node(data);_root->_col = BLACK;//根节点必须是黑色的return make_pair(Iterator(_root, _root), true);}while (pcur){if (kot(data) < kot(pcur->_data)){parent = pcur;pcur = pcur->_left;}else if (kot(data) > kot(pcur->_data)){parent = pcur;pcur = pcur->_right;}else{return make_pair(Iterator(pcur, _root), false);}}pcur = new Node(data);Node* newnode = pcur;//记录新节点指针,旋转可能会更新pcurif (kot(data) < kot(parent->_data)){parent->_left = pcur;}else{parent->_right = pcur;}pcur->_parent = parent;Node* grandfather = parent->_parent;Node* uncle = nullptr;//当父节点存在,且颜色为红,需要往上更新while (parent && parent->_col == RED){if (parent == grandfather->_left){uncle = grandfather->_right;}else{uncle = grandfather->_left;}if (uncle && uncle->_col == RED)//情况一:u存在且为红{parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;if (grandfather->_parent == nullptr){grandfather->_col = BLACK;return make_pair(Iterator(newnode, _root), true);}pcur = grandfather;parent = pcur->_parent;grandfather = parent->_parent;}else //情况二:u存在且为黑 / 情况三:u不存在{if (parent == grandfather->_left && pcur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_left && pcur == parent->_right){RotateL(parent);RotateR(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && pcur == parent->_left){RotateR(parent);RotateL(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}else if (parent == grandfather->_right && pcur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}break;}}return make_pair(Iterator(newnode, _root), true);}private:void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}Node* parentparent = parent->_parent;parent->_parent = subL;if (parentparent == nullptr){_root = subL;}else{if (parent == parentparent->_left){parentparent->_left = subL;}else{parentparent->_right = subL;}}subL->_parent = parentparent;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;Node* parentparent = parent->_parent;parent->_parent = subR;if (parentparent == nullptr){_root = subR;}else{if (parent == parentparent->_left){parentparent->_left = subR;}else{parentparent->_right = subR;}}subR->_parent = parentparent;}Node* _copy(Node* root){if (root == nullptr){return nullptr;}Node* newnode = new Node(root->_kv);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}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;
};

MySet.h:

#pragma once#include "RBTree.h"namespace yjz
{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);}iterator find(const K& key){return _t.Find(key);}private:RBTree<K, const K, SetKeyOfT> _t;};void Print(const set<int>& s){set<int>::iterator it = s.end();while (it != s.begin()){--it;cout << *it << " ";}cout << endl;}void test_set(){set<int> s;int a[] = { 4,2,6,1,3,5,15,7,16,14 };for (auto e : a){s.insert(e);}for (auto e : s){cout << e << " ";}cout << endl;set<int>::iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;set<int>::iterator It = s.end();while (It != s.begin()){--It;cout << *It << " ";}cout << endl;Print(s);}
}

MyMap.h:

#pragma once#include "RBTree.h"namespace yjz
{template<class K, class V>struct map{struct MapKeyOfT{const K& operator()(const pair<const 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<const K, V>& kv){return _t.Insert(kv);}iterator find(const K& key){return _t.Find(key);}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};void test_map(){map<string, string> m;m.insert({ "front", "前" });m.insert({ "behind", "后" });m.insert({ "left", "左" });m.insert({ "right", "右" });map<string, string>::iterator it = m.begin();m[it->first] = "前面";m["sort"] = "排序";m["string"];while (it != m.end()){//it->first += 'x';it->second += 'x';cout << it->first << "->" << it->second << endl;++it;}cout << endl;}
}

本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像

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

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

相关文章

阿里P8面试官推荐学习的11大专题:java面试精讲框架文档

本篇文章给大家分享一波&#xff0c;阿里P8面试官推荐学习的11大专题&#xff1a;java面试精讲框架文档&#xff0c;主要包含11大块的内容&#xff1a;spring、springcloud、netty、zookeeper、kafka、Hadoop、HBASE、Cassandra、elasticsearch、spark、flink&#xff1b;希望大…

【C++入门篇 - 3】:从C到C++第二篇

文章目录 从C到C第二篇new和delete命名空间命名空间的访问 cin和coutstring的基本使用 从C到C第二篇 new和delete 在C中用来向系统申请堆区的内存空间 New的作用相当于C语言中的malloc Delete的作用相当于C语言中的free 注意&#xff1a;在C语言中&#xff0c;如果内存不够…

stm32定时器中断和外部中断

一&#xff0c;中断系统的介绍 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行 中…

Github 优质项目推荐(第七期):涵盖免费服务、API、低代码、安卓root、深度学习

文章目录 Github优质项目推荐 - 第七期一、【LangGPT】&#xff0c;5.7k stars - 让每个人都成为提示专家二、【awesome-selfhosted】&#xff0c;198k stars - 免费软件网络服务和 Web 应用程序列表三、【public-apis】&#xff0c;315k stars - 免费 API四、【JeecgBoot】&am…

No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史

大家好&#xff01;作为一个喜欢探索本质的INTP&#xff0c;我整理了一份简明易懂的Web安全笔记。希望能帮助你轻松掌握这个领域的核心知识。 这份笔记涵盖了Web发展的历程&#xff0c;从静态的Web 1.0到智能化的Web 3.0。我们将探讨URL和HTTP协议&#xff0c;揭示它们在网络中…

新书速览|你好,C++

《你好&#xff0c;C》 本书内容 《你好&#xff0c;C》主要介绍C开发环境的搭建、基础语法知识、面向对象编程思想以及标准模板库的应用&#xff0c;特别针对初学者在学习C过程中可能遇到的难点提供了解决方案。全书共分13章&#xff0c;以一个工资程序的不断优化和完善为线索…

Windows 下 cocos2d-x-3.17.2 VS2017开发环境搭建

1.下载cocos2d-x-3.17.2 源码: Cocos2d-x - 成熟、轻量、开放的跨平台解决方案 2.下载Python2 Python 2.7.0 Release | Python.org 加入环境变量: 测试版本

构建高效作业管理平台:Spring Boot师生协作评审系统

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…

Zigbee2MQTT多控网关开发专题:【第一篇】系统配置与初始化

01 前言 本文章原文发表于我的微信公众号&#xff0c;请大家关注阅读&#xff0c;涉及的源代码等都在公众号&#xff0c;请搜索公众号&#xff1a; 智能家居NodeRed和HomeAssistant 即可关注。 02 概述 基于NodeRed的Zigbee2MQTT多功能多控网关开发专题正式开贴&#xff0c;…

Linux shellcheck工具

安装工具 通过linux yum源下载&#xff0c;可能因为yum源的问题找不到软件包&#xff0c;或者下载的软件包版本太旧。 ShellCheck的源代码托管在GitHub上(推荐下载方式)&#xff1a; GitHub - koalaman/shellcheck: ShellCheck, a static analysis tool for shell scripts 对下…

VSCode搭建C/C++开发环境【Windows】

VSCode搭建C/C开发环境 1. 配置C/C开发环境1.1 下载和配置MinGW-w64编译器套件1.2 安装C/C插件 2. 在VSCode上编写C语言代码&#xff0c;并编译执行2.1 先打开一个文件夹&#xff0c;写一份C语言代码2.2 设置C/C编译的选项&#xff1a;c_cpp_properties.json2.3 创建执行任务&a…

Unity MVC框架1-2 实战分析

该课程资源来源于唐老狮&#xff0c;吃水不忘打井人&#xff0c;不胜感激 Unity MVC框架演示 1-1 理论分析-CSDN博客 首先你需要知道什么mvc框架&#xff0c;并且对三个层级有个比较清晰的认识&#xff0c;当然不清楚也好&#xff0c;下面例子中将会十分细心地让你理解&#x…

光控资本:牛市一般维持多长时间?牛市的轮涨顺序是什么?

牛市继续多长时间没有一个统一标准&#xff0c;我们是无法判断牛市什么时候到来&#xff0c;什么时候结束的。以A股牛市前史为例&#xff0c;继续时间从几十天到几年的情况都有&#xff0c;是没有规则可循的&#xff0c;现在A股继续最久的一次牛市是862天。 纵观A股前史&#…

【路径规划】创建末端执行器的路径,导入URDF模型,使用逆向运动学进行路径规划

摘要 本文通过路径规划为机器人末端执行器生成运动路径&#xff0c;采用URDF&#xff08;Unified Robot Description Format&#xff09;导入机器人模型&#xff0c;并结合逆向运动学进行路径规划和控制。使用Matlab进行建模和仿真&#xff0c;以确保执行器沿预定路径顺利运动…

API调用comfyui工作流,做一个自己的app,chatgpt给我写的前端,一键创建自己的卡通形象,附源码

前言 工具介绍 首先 comfyui你是少不了的&#xff0c;这个是工作流的后端支持&#xff0c;用这个去调试工作流和生成API可调用文件 前端我们就用很流行的gradio吧&#xff0c;什么你一时半会没有学gradio的计划&#xff0c;没事&#xff0c;笔者也没系统学过&#xff0c;我干…

Ubuntu18.04安装cuda11.1(出现c++版本问题)

一、概述 需要使用到ubuntu18.04进行cuda的配置&#xff0c;最新版本的cuda跟pytorch的版本不太适配&#xff0c;所以为了能够复现&#xff0c;我选择了一些老版本的cuda11&#xff0c;其使用的范围更加广泛。将自己 二、具体操作 &#xff08;一&#xff09;安装流程 1.官网…

[Linux#62][TCP] 首位长度:封装与分用 | 序号:可靠性原理 | 滑动窗口:流量控制

目录 一. 认识TCP协议的报头 1.TCP头部格式 2. TCP协议的特点 二. TCP如何封装与分用 TCP 报文封装与解包 如何封装解包&#xff0c;如何分用 分离有效载荷 隐含问题&#xff1a;TCP 与 UDP 报头的区别 封装和解包的逆向过程 如何分用 TCP 报文 如何通过端口号找到绑…

帝国CMS系统开启https后,无法登陆后台的原因和解决方法

今天本地配置好了帝国CMS7.5&#xff0c;传去服务器后&#xff0c;使用http访问一切正常。但是当开启了https&#xff08;SSL&#xff09;后&#xff0c;后台竟然无法登陆进去了。 输入账号密码后&#xff0c;点击登陆&#xff0c;跳转到/e/admin/ecmsadmin.php就变成页面一片…

多线程会在一个事务里面吗?

目录 多线程会在一个事务里面吗&#xff1f; 多线程、数据库事务以及数据库连接之间的关系 Spring的事务管理​​​​​​​ 声明式事务Transactional的实现原理 声明式事务Transactional的失效场景 Transactional注解的方法不是public为什么会失效 Spring AOP的代理机制…

【C++】——继承(下)

【C】——继承&#xff08;下&#xff09; 5 继承与友元6 继承与静态成员7 多继承7.1 继承模型7.2 菱形继承的问题7.3 虚继承7.4 多继承中的指针偏移问题 8 组合与继承 5 继承与友元 友元关系不能被继承。即一个函数是父类的友元函数&#xff0c;但不是子类的友元函数。也就是说…