【C++學習手札】基于紅黑樹封裝模擬實現map和set

?

???? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????🎬慕斯主頁修仙—別有洞天

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?💜本文前置知識:?紅黑樹

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????? ??今日夜電波:漂流—菅原紗由理

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2:55━━━━━━?💟──────── 4:29
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????🔄 ? ?? ? ? ? ?? ? ????

??????????????????????????????????????💗關注👍點贊🙌收藏您的每一次鼓勵都是對我莫大的支持😍


目錄

一、前言

?????????map和set的底層原理????????

?二、紅黑樹的封裝

? ? ? ? ?通過模板使得map和set都可復用紅黑樹

? ? ? ? ?迭代器類

????????operator++()

????????operator--()?

? ? ? ? 紅黑樹類?

? ? ? ? 仿函數

????????map?

? ? ? ? set

? ? ? ? ?封裝后的紅黑樹

? ? ? ? ?begin()和end()

? ? ? ? ?通過仿函數來控制要比較的值

? ? ? ? ?完整封裝?

三、map和set的封裝

????????封裝后的set?

? ? ? ? 封裝后的map?

?四、完整代碼

? ? ? ? RBTree.h

? ? ? ? myset.h?

? ? ? ? mymap.h


一、前言

? ? ? ? ?本文主要敘述基于紅黑樹對于map和set的封裝實現,需要有紅黑樹的知識前提。由于前面作者對于紅黑樹主要只是模擬實現了插入的功能。因此本文也只是實現map和set相應的功能,本文的主要要點在于map和set的封裝以及迭代器中++和--的實現

?????????map和set的底層原理????????

????????C++中的map和set都是STL中的關聯容器,都基于紅黑樹實現。其中set是K模型的容器,而map是KV模型的容器,本文主要講述用一棵KV模型的紅黑樹同時實現map和set。map和set都使用紅黑樹的基本操作,時間復雜度為O(log n),其中n為元素數量。因此,map和set都是高效的關聯容器。

?二、紅黑樹的封裝

? ? ? ? ?通過模板使得map和set都可復用紅黑樹

? ? ? ? 可以看到我們定義了一個模板參數T,通過T的類型變化來改變紅黑樹中每一個節點的值,從而控制整顆紅黑樹的復用。?

enum Colour
{RED,BLACK
};template<class T>
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){}
};

? ? ? ? ?迭代器類

? ? ? ?迭代器實際上是對于指針進行操作,因此我們實例化并且重新命名了節點類的指針Node,由于迭代器分為是否常量迭代器,對此我們額外定義了兩個模板參數Ref、Ptr用于控制其中重載運算符 T& operator*() 和?T* operator->()當我們實例化時,區分Ref是const T&還是T&、Ptr是const T*還是T*后面RBTree中會有所體現。在迭代器中,其中,operator*和operator->返回指向節點的指針,operator++和operator--實現前后綴++/--運算符,operator==和operator!=用來比較兩個迭代器是否指向同一個節點。?

? ? ? ? 以下為大致實現的功能:

template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef RBTreeNode<T> Node;typedef __TreeIterator<T, Ref, Ptr> Self;Node* _node;__TreeIterator(Node* node):_node(node){}Self& operator--();Self& operator++();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;}};

????????operator++()

????????對于map和set的遍歷我們默認都是中序遍歷,也就是左子樹 根 右子樹。因此對于++操作我們首要的是找到下一個節點,則這個下一個節點便是在這個節點的右子樹,也就是而下一個節點的準確位置為:這個節點的右子樹的最左節點(為什么呢?因為左 根?右我們將這個節點看作為根,則下一個節點位置為右子樹,而右子樹的第一個節點則為最左的節點)。?當這個節點的右為空,意味著包括這個節點在內的左 根 右都遍歷完了,那么我們就需要向上遍歷。則需遵循以下:如果孩子是父親的左就返回父親(這就是意味著遍歷完了左 接下來要遍歷 根),否則就繼續向上遍歷,如果走到nullptr那就是遍歷完成。

總結一下遍歷規則:

1、如果_node的右不為空,找右孩子的最左節點

2、如果_node的右為空,如果孩子是父親的左就返回父親,否則就繼續向上遍歷,如果走到nullptr那就是遍歷完成

	Self& operator++(){if (_node->_right){// 下一個就是右子樹的最左節點Node* cur = _node->_right;while (cur->_left){cur = cur->_left;}_node = cur;}else{// 左子樹 根 右子樹// 右為空,找孩子是父親左的那個祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}

????????operator--()?

? ? ? ? ?和上面的operator++()相似,但是我們的遍歷順序變為了右子樹 根 左子樹。

總結一下遍歷規則:

1、如果_node的左不為空,找左孩子的最右節點

2、如果_node的左為空,如果孩子是父親的右就返回父親,否則就繼續向上遍歷,如果走到nullptr那就是遍歷完成

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

? ? ? ? 紅黑樹類?

? ? ? ? ?從之前我們所學習的紅黑樹的模擬實現我們可以知道,紅黑樹的插入等等操作中會用到對于key的比較。對此,set和map的比較要求是不同的,set可以直接用key進行比較,而map中對于pair的比較是先按first比較再比價second,而我們想要的結果是只比較first,因此我們定義了個KeyofT來對map和set進行區分。這個KeyofT則是通過傳遞仿函數來進行控制對于要比較值的轉換。

// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<const K, T>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __TreeIterator<T, T&, T*> iterator;typedef __TreeIterator<T, const T&, const T*> const_iterator;iterator begin();iterator end();const_iterator begin();const_iterator end();//pair<iterator, bool> Insert(const T& data)pair<Node*, bool> Insert(const T& data);Node * Find(const K & key)private:Node* _root = nullptr;
};

? ? ? ? 仿函數

????????注意:這里的仿函數是在map和set中定義的,我們在map和set中的迭代器實際上是就是間接的控制了RBTree的迭代器。

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

? ? ? ? ?封裝后的紅黑樹

? ? ? ? ?begin()和end()

?????????STL明確規定,begin()與end()代表的是一段前閉后開的區間,而對紅黑樹進行中序遍歷后,可以得到一個有序的序列,因此:begin()可以放在紅黑樹中最小節點(即最左側節點)的位置,end()放在最大節點(最右側節點)的下一個位置,關鍵是最大節點的下一個位置在哪塊?能否給成nullptr呢?答案是行不通的,因為對end()位置的迭代器進行--操作,必須要能找最后一個元素,此處就不行,因此最好的方式是將end()放在頭結點的位置:

? ? ? ? ?雖然但是,作者還是將end()給了nullptr,事實上勉強還是可以用的哈哈哈...

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

? ? ? ? ?通過仿函數來控制要比較的值

? ? ? ? ?注意:這里對于insert以及find中都定義了一個KeyOfT kot; 這個就是上面所提到的用于轉化用于比較的數據的仿函數的定義。

?????????其中對于insert有點需要注意:我們運用了pair中的特性,用pair<Node*, bool>接收了make_pair(newnode, true)的返回值,用pair構造了一個新的pair而不是拷貝構造了一個pair后續會提到為什么(在set封裝中)

	//pair<iterator, bool> Insert(const T& data)pair<Node*, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* parent = nullptr;Node* cur = _root;KeyOfT kot;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(cur, false);}}// 新增節點給紅色cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//     g//   p   u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上更新處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){// 單旋//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 雙旋//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else  // parent == grandfather->_right{//     g//   u   p //          c//Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   u   p //     c//RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(newnode, true);}Node * Find(const K & key){Node* cur = _root;KeyOfT kot;while (cur!= nullptr){	if (kot(cur->_data) < key){cur = cur->_left;}else if (kot(cur->_data) > key){cur = cur->_right;}else{return cur;}}return nullptr;}

? ? ? ? 完整封裝?
// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<const K, T>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __TreeIterator<T, T&, T*> iterator;typedef __TreeIterator<T, const T&, const T*> const_iterator;iterator begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}//pair<iterator, bool> Insert(const T& data)pair<Node*, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* parent = nullptr;Node* cur = _root;KeyOfT kot;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(cur, false);}}// 新增節點給紅色cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//     g//   p   u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上更新處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){// 單旋//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 雙旋//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else  // parent == grandfather->_right{//     g//   u   p //          c//Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   u   p //     c//RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(newnode, true);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL)subRL->_parent = parent;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}// 根節點->當前節點這條路徑的黑色節點的數量bool Check(Node* root, int blacknum, const int refVal){if (root == nullptr){//cout << balcknum << endl;if (blacknum != refVal){cout << "存在黑色節點數量不相等的路徑" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "有連續的紅色節點" << endl;return false;}if (root->_col == BLACK){++blacknum;}return Check(root->_left, blacknum, refVal)&& Check(root->_right, blacknum, refVal);}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;//參考值int refVal = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refVal;}cur = cur->_left;}int blacknum = 0;return Check(_root, blacknum, refVal);}int Height(){return _Height(_root);}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;}size_t Size(){return _Size(_root);}size_t _Size(Node* root){if (root == NULL)return 0;return _Size(root->_left)+ _Size(root->_right) + 1;}Node * Find(const K & key){Node* cur = _root;KeyOfT kot;while (cur!= nullptr){	if (kot(cur->_data) < key){cur = cur->_left;}else if (kot(cur->_data) > key){cur = cur->_right;}else{return cur;}}return nullptr;}private:Node* _root = nullptr;
};

三、map和set的封裝

????????封裝后的set?

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

? ? ? ? ?注意這段代碼:

typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

? ? ? ? 其中typenam是告訴編譯器這里是類型因為這里是對類模板取內嵌類型。通過set的定義我們知道set不允許被修改數值,因此我們將兩個迭代器實際上都定義為const_iterator。但是這樣定義其中insert又出問題了,因為其中的返回類型會出現不匹配的情況,即pair<iterator, bool> 和_t.Insert(key)不匹配。因為我們return返回的實際上是iterator,而實際上接受的類型為const_iterator。這時我們上面提到的用pair構造了一個新的pair而不是拷貝構造了一個pair就起到作用了,他使得返回的類型匹配!

? ? ? ? 當然我們也有其他的解決辦法:定義一個迭代器的拷貝構造?

????????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){}
}

?????????

? ? ? ? 封裝后的map?

????????想較于set,map的key值不可修改,但是value是可以修改的,對于他的迭代器定義按照正常的const和非const就好,但是他主要做文章的地方是在RBTree<K, pair<const K, V>, MapKeyOfT> _t;中,直接將K定義為const K了。??

#pragma once
#include"RBTree.h"namespace bit
{template<class K, class V>class map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};// 對類模板取內嵌類型,加typename告訴編譯器這里是類型typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RBTree<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();}const_iterator end()const{return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}

?四、完整代碼

? ? ? ? ?RBTree.h
#pragma once// set ->key
// map ->key/valueenum Colour
{RED,BLACK
};template<class T>
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, class Ref, class Ptr>
struct __TreeIterator
{typedef RBTreeNode<T> Node;typedef __TreeIterator<T, Ref, Ptr> Self;Node* _node;__TreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator--(){if (_node->_left){Node* cur = _node->_left;while (cur->_right){cur = cur->_right;}_node = cur;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator++(){if (_node->_right){// 下一個就是右子樹的最左節點Node* cur = _node->_right;while (cur->_left){cur = cur->_left;}_node = cur;}else{// 左子樹 根 右子樹// 右為空,找孩子是父親左的那個祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<const K, T>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __TreeIterator<T, T&, T*> iterator;typedef __TreeIterator<T, const T&, const T*> const_iterator;iterator begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}//pair<iterator, bool> Insert(const T& data)pair<Node*, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* parent = nullptr;Node* cur = _root;KeyOfT kot;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(cur, false);}}// 新增節點給紅色cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//     g//   p   u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上更新處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){// 單旋//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 雙旋//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else  // parent == grandfather->_right{//     g//   u   p //          c//Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   u   p //     c//RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(newnode, true);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL)subRL->_parent = parent;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}// 根節點->當前節點這條路徑的黑色節點的數量bool Check(Node* root, int blacknum, const int refVal){if (root == nullptr){//cout << balcknum << endl;if (blacknum != refVal){cout << "存在黑色節點數量不相等的路徑" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "有連續的紅色節點" << endl;return false;}if (root->_col == BLACK){++blacknum;}return Check(root->_left, blacknum, refVal)&& Check(root->_right, blacknum, refVal);}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;//參考值int refVal = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refVal;}cur = cur->_left;}int blacknum = 0;return Check(_root, blacknum, refVal);}int Height(){return _Height(_root);}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;}size_t Size(){return _Size(_root);}size_t _Size(Node* root){if (root == NULL)return 0;return _Size(root->_left)+ _Size(root->_right) + 1;}Node * Find(const K & key){Node* cur = _root;KeyOfT kot;while (cur!= nullptr){	if (kot(cur->_data) < key){cur = cur->_left;}else if (kot(cur->_data) > key){cur = cur->_right;}else{return cur;}}return nullptr;}private:Node* _root = nullptr;
};

? ? ? ? myset.h?
pragma once
#include"RBTree.h"namespace bit
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin() const{return _t.begin();}iterator end() const{return _t.end();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}

? ? ? ? mymap.h
#pragma once
#include"RBTree.h"namespace bit
{template<class K, class V>class map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};// 對類模板取內嵌類型,加typename告訴編譯器這里是類型typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RBTree<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();}const_iterator end()const{return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}


??????????????????????????感謝你耐心的看到這里?( ′・?・` )比心,如有哪里有錯誤請踢一腳作者o(╥﹏╥)o!?

????????????????????????????????? ? ? ?

?????????????????????????????????????????????????????????????????????????給個三連再走嘛~??

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/209125.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/209125.shtml
英文地址,請注明出處:http://en.pswp.cn/news/209125.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Appium獲取toast方法封裝

一、前置說明 toast消失的很快&#xff0c;并且通過uiautomatorviewer也不能獲取到它的定位信息&#xff0c;如下圖&#xff1a; 二、操作步驟 toast的class name值為android.widget.Toast&#xff0c;雖然toast消失的很快&#xff0c;但是它終究是在Dom結構中出現過&…

【計算機網絡】HTTP請求

目錄 前言 HTTP請求報文格式 一. 請求行 HTTP請求方法 GET和POST的區別 URL 二. 請求頭 常見的Header 常見的額請求體數據類型 三. 請求體 結束語 前言 HTTP是應用層的一個協議。實際我們訪問一個網頁&#xff0c;都會像該網頁的服務器發送HTTP請求&#xff0c;服務…

使用Java將圖片添加到Excel的幾種方式

1、超鏈接 使用POI&#xff0c;依賴如下 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency>Java代碼如下,運行該程序它會在桌面創建ImageLinks.xlsx文件。 …

GPT-4V 在機器人領域的應用

在科技的浩渺宇宙中&#xff0c;OpenAI如一顆璀璨的星辰&#xff0c;于2023年9月25日&#xff0c;以一種全新的方式&#xff0c;向世界揭示了其最新的人工智能力作——GPT-4V模型。這次升級&#xff0c;為其旗下的聊天機器人ChatGPT裝配了語音和圖像的新功能&#xff0c;使得用…

『Linux升級路』進度條小程序

&#x1f525;博客主頁&#xff1a;小王又困了 &#x1f4da;系列專欄&#xff1a;Linux &#x1f31f;人之為學&#xff0c;不日近則日退 ??感謝大家點贊&#x1f44d;收藏?評論?? 目錄 一、預備知識 &#x1f4d2;1.1緩沖區 &#x1f4d2;1.2回車和換行 二、倒計…

修改正點原子綜合實驗的NES模擬器按鍵控制加橫屏

??????? 開發板&#xff1a;stm32f407探索者開發板V2 屏幕是4.3寸-800-480-MCU屏 手頭沒有V3開發板&#xff0c;只有V2&#xff0c;所以沒法測試 所以只講修改哪里&#xff0c;請自行修改 先改手柄部分&#xff0c;把手柄改成按鍵 找到左邊的nes文件夾中的nes_mai…

采用軌到軌輸出設計 LTC6363HMS8-2、LTC6363HMS8-1、LTC6363HRD、LTC6363IDCB差分放大器I

產品詳情 LTC6363 系列包括四個全差分、低功耗、低噪聲放大器&#xff0c;具有經優化的軌到軌輸出以驅動 SAR ADC。LTC6363 是一款獨立的差分放大器&#xff0c;通常使用四個外部電阻設置其增益。LTC6363-0.5、LTC6363-1 和 LTC6363-2 都有內部匹配電阻&#xff0c;可分別創建…

【Python百寶箱】代碼沖突?文件合并不再是問題!Python解決方案大揭秘

Python腳本與圖形工具&#xff1a;文件比較與合并的完整指南 前言 在軟件開發、版本控制和數據處理領域&#xff0c;文件比較和合并是至關重要的任務。Python生態系統中涌現了許多強大的工具和庫&#xff0c;為開發者提供了豐富的選擇。本指南將深入探討 Python 中常用的文件…

看完了一個動畫電影-心靈奇旅

refer: 開二倍速看完了&#xff0c;一部分是聽的&#xff0c;劇情還可以&#xff0c;就是普通的治愈片。 里邊有個臺詞&#xff1a; 一條小魚游到一條老魚旁邊說,“我要找到他們稱之為海洋的東西。” “海洋?”老魚問,“你現在就在海洋里啊。” “這兒?”小魚說,“這兒是水…

人工智能:走向未來的智慧之路

1. 定義與范疇 人工智能&#xff08;AI&#xff09;是一門研究如何使計算機系統能夠模擬人類智慧的科學與技術。這包括了機器學習、深度學習、自然語言處理、計算機視覺等多個子領域。機器學習讓計算機能夠通過數據學習&#xff0c;而深度學習則通過模擬人腦神經網絡的方式實現…

C++數據結構:B樹

目錄 一. 常見的搜索結構 二. B樹的概念 三. B樹節點的插入和遍歷 3.1 插入B樹節點 3.2 B樹遍歷 四. B樹和B*樹 4.1 B樹 4.2 B*樹 五. B樹索引原理 5.1 索引概述 5.2 MyISAM 5.3 InnoDB 六. 總結 一. 常見的搜索結構 表示1為在實際軟件開發項目中&#xff0c;常用…

博途PLC SCL間接尋址編程應用

這篇博客里我們將要學習Pointer和Any指針&#xff0c;PEEK和POKE指令&#xff0c;當然我們還可以數組類型數據實現數組指針尋址&#xff0c;具體應用介紹請參考下面文章鏈接&#xff1a; https://rxxw-control.blog.csdn.net/article/details/134761364https://rxxw-control.b…

一文講解如何從 Clickhouse 遷移數據至 DolphinDB

ClickHouse 是 Yandex 公司于2016年開源的 OLAP 列式數據庫管理系統&#xff0c;主要用于 WEB 流量分析。憑借面向列式存儲、支持數據壓縮、完備的 DBMS 功能、多核心并行處理的特點&#xff0c;ClickHouse 被廣泛應用于廣告流量、移動分析、網站分析等領域。 DolphinDB 是一款…

【Hadoop_02】Hadoop運行模式

1、Hadoop的scp與rsync命令&#xff08;1&#xff09;本地運行模式&#xff08;2&#xff09;完全分布式搭建【1】利用102將102的文件推到103【2】利用103將102的文件拉到103【3】利用103將102的文件拉到104 &#xff08;3&#xff09;rsync命令&#xff08;4&#xff09;xsync…

使用 HTML 地標角色提高可訪問性

請務必確保所有用戶都可以訪問您的網站&#xff0c;包括使用屏幕閱讀器等輔助技術的用戶。 一種方法是使用 ARIA 地標角色來幫助屏幕閱讀器用戶輕松瀏覽您的網站。使用地標角色還有其他好處&#xff0c;例如改進 HTML 的語義并更輕松地設置網站樣式。在這篇博文中&#xff0c;我…

深度探索Linux操作系統 —— 構建initramfs

系列文章目錄 深度探索Linux操作系統 —— 編譯過程分析 深度探索Linux操作系統 —— 構建工具鏈 深度探索Linux操作系統 —— 構建內核 深度探索Linux操作系統 —— 構建initramfs 文章目錄 系列文章目錄前言一、為什么需要 initramfs二、initramfs原理探討三、構建基本的init…

tomcat篇---第二篇

系列文章目錄 文章目錄 系列文章目錄前言一、tomcat容器是如何創建servlet類實例?用到了什么原理?二、tomcat 如何優化?三、熟悉tomcat的哪些配置?前言 前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到網站,這篇文章男女…

Web應用JSON數據保護(密碼算法、密鑰、數字簽名和數據加密)

1.JSON&#xff08;JavaScript Object Notation&#xff09; JSON是一種輕量級的數據交換格式&#xff0c;采用完全獨立于編程語言的文本格式來存儲和表示數據。JSON通過簡單的key-value鍵值對來描述數據&#xff0c;可以被廣泛用于網絡通信、數據存儲等各種應用場景&#xff0…

【重點】Flink四大基石

1. Time&#xff08;時間機制&#xff09; 時間概念 處理時間&#xff1a;執行具體操作時的機器時間&#xff08;例如 Java的 System.currentTimeMillis()) &#xff09;事件時間&#xff1a;數據本身攜帶的時間&#xff0c;事件產生時的時間。攝入時間&#xff1a;數據進入 …

代碼隨想錄算法訓練營第四十六天 _ 動態規劃_198.打家劫舍、213.打家劫舍II、337.打家劫舍 III。

學習目標&#xff1a; 動態規劃五部曲&#xff1a; ① 確定dp[i]的含義 ② 求遞推公式 ③ dp數組如何初始化 ④ 確定遍歷順序 ⑤ 打印遞歸數組 ---- 調試 引用自代碼隨想錄&#xff01; 60天訓練營打卡計劃&#xff01; 學習內容&#xff1a; 198.打家劫舍 動態規劃五步曲&a…