前言
前面我們已經對紅黑樹做了介紹和實現,本期我們來對紅黑樹進一步改造,然后基于改造后的紅黑樹封裝出map和set!
本期內容介紹
? 紅黑樹的改造
? 紅黑樹的迭代器實現
? map的封裝
? set的封裝
? 全部源碼
● 紅黑樹的改造
我們目前的實現的紅黑樹,里面存的是一個pair<K,V>的鍵值對,但是我們知道set是只有key沒有val的,map是<key,val>的!而他兩都是基于紅黑樹實現的,難道是弄兩棵紅黑樹分別封裝?這是不是太cuo了呀!顯然不是的,庫里面他兩底層用的是一顆紅黑樹:
這里人家是將底層存的數據沒有寫死,給了一個Value的類型!你上層如果封裝成map就給pair<K,V>,如果是封裝成set就給K即可!(后面的比較器和空間配置器暫時就不看了)
? 問題一:set使用時是一個key為什么底層也要傳給紅黑樹兩個key?
由于map和set的底層都用的是同一棵紅黑樹,而map存的是K,V類型的鍵值對,所以這里可以認為是set對map的遷就!所以set即使用的時候只是一個key,但底層還是給紅黑樹一樣的V和K,目的是使得紅黑樹的模板參數的一致性!
? 問題二:紅黑樹的是在左插入等操作時,如何知道你是pair還是key呢?
的確紅黑樹那一層是不知道的,但是我們map和set層是知道的!如何讓紅黑樹那一層知道呢?就是第三個參數,是一個獲取存儲值key的類;當map和set傳過去之后,紅黑樹層可以創建對象獲取!
OK,我們也改造一下自己的紅黑樹出來吧:(我們就把存儲的數據類型改成T避免與V混淆)
節點類要就不在是K,V了,而是T了:
insert也是不再是插入(inert后面介紹到[]還會改造)
OK,我們先把紅黑樹的構造和拷貝構造以及賦值拷貝給整出來,在上層例如set進行賦值,拷貝等操作,就會到紅黑樹這一層,如果紅黑樹沒有就成了淺拷貝了!
? 默認構造
這里本來可以不用寫的,但是后面如果實現了賦值拷貝后就自動生不成了,所以得寫!
RBTree():_root(nullptr)
{}
還有一種就是即使有了拷貝構造可以強制讓編譯器生成:
RBTree() = default;//強制讓編譯器生成默認構造
? 拷貝構造
這里采用二叉樹的前序逐一拷貝即可:
RBTree(const RBTree<K, T, KofT>& t)
{_root = Copy(t._root);
}
Node* Copy(Node* root)
{if (root == nullptr)return nullptr;//復制節點和顏色Node* newnode = new Node(root->_data);newnode->_col = root->_col;//復制左孩子newnode->_left = Copy(root->_left);if (newnode->_left)//連接newnode->_left->_parent = newnode;//復制右孩子newnode->_right = Copy(root->_right);if (newnode->_right)//連接newnode->_right->_parent = newnode;return newnode;
}
? 賦值拷貝
直接采用以前的那種現在寫法:將形參用值接受,然后與當前交換
RBTree<K, T, KofT>& operator=(RBTree<K, T, KofT> t)
{swap(_root, t._root);
}
? 析構函數
采用二叉樹的后序,先刪除做孩子,再刪除右孩子,最后刪除根節點!
~RBTree()
{Destory(_root);_root = nullptr;
}
void Destory(Node* root)
{if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;
}
●?紅黑樹的迭代器實現
紅黑樹的迭代器和鏈表的一樣,空間不是連續的無法用原生指針來實現,得改一個類來達到“連續”的行為!我們使用的迭代器無非就begin、end、!=, *,->,++等但是begin和end只有紅黑樹知道,所以我們在迭代器里面只需要實現!=, *, ->,++等即可:
迭代器本質就是也即是一個節點的指針,所以這里的迭代器類和鏈表的一模一樣:
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*
直接返回該節點的數據的引用即可!
Ref operator*()
{return _node->_data;
}
operator->
直接返回當前節點的數據的地址(指針)即可!
Ptr operator->()
{return &_node->_data;
}
operator !=
直接比較兩個節點的地址是否不相等即可!
bool operator!=(const Self& s)
{return _node != s._node;
}
operator==
直接比較兩個節點的地址是否相同即可!
bool operator==(const Self& s)
{return _node == s._node;
}
operator++
實現思路:因為紅黑樹迭代器走的是中序,即 左->根->右!而*的時候已經訪問了該節點的左和他本身++是去找當前節點的下一個節點!所以此時只需要考慮,當前節點的有節點是是否為空!
1、如果當前節點的右節點不為空,下一個要訪問的節點就是,右子樹的最左節點!
2、如果當前節點的右節點為空,說當樹全部都訪問完了,此時就得向上找當前節點是父節點的左的父節點!下一個訪問的就是這個父節點!
Self& operator++()
{if (_node->_right){Node* leftMin = _node->_right;while (leftMin->_left){leftMin = leftMin->_left;}_node = leftMin;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;
}
operator++(int)
有了前置的++后置直接復用即可!后置++的特點是返回+1前的值,這里同理!我們先拷貝一份當前的迭代器,然后當前迭代器復用前置++到下一個要訪問的節點,然后返回拷貝即可!
//后置++
Self operator++(int)
{Self tmp = *this;++(*this);return tmp;
}
operator--
前置++的思路是更具中序的:左->根->右!這里的--就和前置++相反:右->根->左!
1、當前節點的左不為空,下一個訪問的節點就是當前節點左子樹的最右節點!
2、當前節點的左為空,找當前節點是父節點的有孩子的父節點,下一個訪問的是該父節點
// 前置--
Self& operator--()
{if (_node->_left){Node* cur = _node->_left;while (cur->_right){cur = cur->_right;}_node = cur;}else{Node* cur = _node, * parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;
}
operator--(int)
后置--同理直接先拷貝一份當前節點,然后調用前置--帶下一個訪問的節點,最后返回該拷貝即可!
//后置--
Self operator--(int)
{Self tmp = *this;--(*this);return tmp;
}
begin
因為迭代器是按照中序走的,所以begin是整棵樹的最左節點!
Iterator Begin()
{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);
}
end
這里的end應該是最右節點的下一個節點,這里簡單處理為nullptr;
Iterator End()
{return Iterator(nullptr);
}
const_begin
ConstIterator Begin() const
{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return ConstIterator(leftMin);
}
const_end
ConstIterator End() const
{return ConstIterator(nullptr);
}
?● map的封裝
OK,先搞一個框架出來:
#pragma oncenamespace cp
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const prai<K,V>& kv){return kv.first;}};public:private:RBTree<K, pair<const K, V>, MapKeyOfT> _m;//map的key不允許修改};
}
? 注意:map的key是不允許修改的所以用const修飾
OK,先來整迭代器出來,直接調用干紅黑樹的即可:
iterator
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 _m.Begin();
}iterator end()
{return _m.End();
}const_iterator begin() const
{return _m.Begin();
}const_iterator end() const
{return _m.End();
}
注意:
typedef typename RBTree<K, const K, SetKofT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKofT>::ConstIterator const_iterator;這里的RBTree<K, const K, SetKofT>只是類型不是實體,所以編譯器不認識,得加上typename告訴編譯器這是類型;
find
iterator find(const K& key)
{return _m.Find(key);
}
insert
這里的insert,當前的紅黑樹是不要滿足的,因為我們以前介紹使用的時候介紹過它的返回值是一個pair<iterator, bool>,如果插入成功返回當前插入節點的迭代器,bool設置為true;如果插入的元素失敗,返回當前存在元素的迭代器,bool是false;
所以,我么先得改造紅黑樹的insert:掛在燥起來也不難,就是將返回值換成pair<iterator, bool>即可,唯一注意的一個點就是最后返回的那個節點的迭代器;一定要在前面提前記錄變色前的那個新節點的指針,方便后面構造迭代器;如果不記錄后面直接返回cur這個cur就不一定是前面的cur了因為會旋轉 + 變色改變!!!
pair<Iterator, bool> Insert(const T& data)
{//第一次插入if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;//根節點是黑色return make_pair(Iterator(_root), true);}Node* cur = _root;//當前節點Node* parent = nullptr;//當前節點的父節點KofT 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(Iterator(cur), false);//插入的值已經存在}}//找到了插入位置cur = new Node(data);Node* newnode = cur;//提前保存返回的元素的位置,方便構造迭代器//鏈接if (kot(cur->_data) > kot(parent->_data)){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;//叔叔在爺爺的右//叔叔存在且為紅 -> 變色if (uncle && uncle->_col == RED){grandfather->_col = RED;parent->_col = uncle->_col = BLACK;//繼續更新cur = grandfather;parent = cur->_parent;}else//叔叔不存在或存在在且為黑 --> 旋轉 + 變色{if (cur == parent->_left){// g// p u// cRotateR(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){grandfather->_col = RED;parent->_col = uncle->_col = BLACK;//繼續更新cur = grandfather;parent = cur->_parent;}else//叔叔不存在或為黑 --> 旋轉+變色{if (cur == parent->_left){// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateL(grandfather);parent->_col = BLACK;//變色grandfather->_col = RED;}break;//旋轉+變色完了就結束掉}}}_root->_col = BLACK;//保證根節點永遠是黑色return make_pair(Iterator(newnode), true);
}
成功改造完紅黑的的insert之后map和set的就直接可以調用了!
pair<iterator, bool> insert(const pair<K, V>& kv)
{return _m.Insert(kv);
}
operator[]
[]是基于insert實現的,在介紹使用的時候介紹過,[]按照K值插入,不管插入成功與否,都返回V的引用~!
V& operator[](const K& key)
{pair<iterator, bool> ret = _m.Insert(make_pair(key, V()));return ret.first->second;
}
OK,測試一下:
void Print_map(const cp::map<string, int> m)
{for (auto& e : m){cout << e.first << " : " << e.second << endl;}
}void TestMap()
{cp::map<int, int> m1;m1.insert({ 1,1 });m1.insert({ 2,2 });m1.insert({ 3,3 });m1.insert({ 4,4 });m1.insert({ 5,5 });cp::map<int, int>::iterator it = m1.begin();while (it != m1.end()){cout << it->first << ":" << it->second << endl;it++;}cout << "--------------------" << endl;cp::map<string, int> m2;string s[] = { "aaa", "bbb", "ccc", "bbb", "cpdd", "000", "蘋果", "西瓜", "aaa" };for (auto& e : s)m2[e]++;Print_map(m2);
}
● set的封裝
同理,我們可以搭建出一個set的基本框架出來:
#pragma oncenamespace cp
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:private:RBTree<K, K, SetKeyOfT> _s;};
}
OK,先來整迭代器出來,直接調用干紅黑樹的即可:
inerator
typedef typename RBTree<K, const K, SetKofT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKofT>::ConstIterator const_iterator;iterator begin()
{return _s.Begin();
}iterator end()
{return _s.End();
}const_iterator begin() const
{return _s.Begin();
}const_iterator end() const
{return _s.End();
}
find
iterator find(const K& key)
{return _s.Find(key);
}
insert
pair<iterator, bool> insert(const K& key)
{return _s.Insert(key);
}
OK,測試一下:
void Print_set(const cp::set<int>& s)
{for (auto& e : s){cout << e << endl;}
}void TestSet()
{vector<int> v = { 8, 3, 1, 10, 6, 4, 7, 14, 13, 8, 6 };cp::set<int> s;for (auto& e : v){s.insert(e);}Print_set(s);
}
OK,到這里map和set的封裝就已經實現完了!一些接口例如size、clear等前面實現過很多次這里就不在實現了!這里想說的是map和set就是套了一層紅黑樹的殼,真正的核心還是紅黑!
● 全部源碼
RBTree.h
#pragma once#pragma onceenum Col
{RED = 0,BLACK
};template <class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Col _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED)//默認新的節點是紅色{}
};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){}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;}//前置++Self& operator++(){if (_node->_right){Node* leftMin = _node->_right;while (leftMin->_left){leftMin = leftMin->_left;}_node = leftMin;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//后置++Self operator++(int){Self tmp = *this;++(*this);return tmp;}// 前置--Self& operator--(){if (_node->_left){Node* cur = _node->_left;while (cur->_right){cur = cur->_right;}_node = cur;}else{Node* cur = _node, * parent = cur->_parent;while (parent && parent->_right != cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//后置--Self operator--(int){Self tmp = *this;--(*this);return tmp;}
};template <class K, class T, class KofT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef _RBTree_Iterator<T, T&, T*> Iterator;typedef _RBTree_Iterator<T, const T&, const T*> ConstIterator;RBTree() = default;//強制讓編譯器生成默認構造//RBTree()// :_root(nullptr)//{}RBTree(const RBTree<K, T, KofT>& t){_root = Copy(t._root);}RBTree<K, T, KofT>& operator=(RBTree<K, T, KofT> t){swap(_root, t._root);}~RBTree(){Destory(_root);_root = nullptr;}Iterator Begin(){Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);}Iterator End(){return Iterator(nullptr);}ConstIterator Begin() const{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return ConstIterator(leftMin);}ConstIterator End() const{return ConstIterator(nullptr);}Iterator Find(const K& key){KofT kot;Node* cur = _root;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return Iterator(cur);}}return End();}pair<Iterator, bool> Insert(const T& data){//第一次插入if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;//根節點是黑色return make_pair(Iterator(_root), true);}Node* cur = _root;//當前節點Node* parent = nullptr;//當前節點的父節點KofT 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(Iterator(cur), false);//插入的值已經存在}}//找到了插入位置cur = new Node(data);Node* newnode = cur;//提前保存返回的元素的位置,方便構造迭代器//鏈接if (kot(cur->_data) > kot(parent->_data)){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;//叔叔在爺爺的右//叔叔存在且為紅 -> 變色if (uncle && uncle->_col == RED){grandfather->_col = RED;parent->_col = uncle->_col = BLACK;//繼續更新cur = grandfather;parent = cur->_parent;}else//叔叔不存在或存在在且為黑 --> 旋轉 + 變色{if (cur == parent->_left){// g// p u// cRotateR(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){grandfather->_col = RED;parent->_col = uncle->_col = BLACK;//繼續更新cur = grandfather;parent = cur->_parent;}else//叔叔不存在或為黑 --> 旋轉+變色{if (cur == parent->_left){// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateL(grandfather);parent->_col = BLACK;//變色grandfather->_col = RED;}break;//旋轉+變色完了就結束掉}}}_root->_col = BLACK;//保證根節點永遠是黑色return make_pair(Iterator(newnode), true);}void InOrder(){return _InOrder(_root);}bool IsBalance(){if (_root && _root->_col == RED)return false;//根節點不可能為紅int black = 0;//根節點到任意一條從根節點到葉子節點的黑色節點的數目Node* cur = _root;while (cur){if (cur->_col == BLACK)black++;cur = cur->_left;}return Check(_root, black, 0);}private:Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newnode = new Node(root->_data);newnode->_col = root->_col;newnode->_left = Copy(root->_left);if (newnode->_left)newnode->_left->_parent = newnode;newnode->_right = Copy(root->_right);if (newnode->_right)newnode->_right->_parent = newnode;return newnode;}void Destory(Node* root){if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}bool Check(Node* root, const int black, int num){if (root == nullptr){if (num != black)return false;return true;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "存在連續的紅色節點:" << endl;return false;}if (root->_col == BLACK){++num;}return Check(root->_left, black, num) && Check(root->_right, black, num);}void RotateR(Node* parent){Node* subL = parent->_left;//父親的左Node* subLR = subL->_right;//左子樹的右Node* ppNode = parent->_parent;//parent的父節點,方便旋轉后的鏈接parent->_left = subLR;//將左子樹的右給父親的做if (subLR)subLR->_parent = parent;subL->_right = parent;//parent做左子樹的右parent->_parent = subL;if (parent == _root)//parent是根{_root = subL;//此時的新根就是subL_root->_parent = ppNode;}else//parent不是根{//將新的根連接到ppNodeif (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void RotateL(Node* parent){Node* subR = parent->_right;//父親的右Node* subRL = subR->_left;//右子樹的左Node* ppNode = parent->_parent;//parent的父節點,方便旋轉后的鏈接parent->_right = subRL;//將右子樹的左連接到parent的右if (subRL)subRL->_parent = parent;subR->_left = parent;//parent連接到subR的左parent->_parent = subR;if (parent == _root)//parent是根{_root = subR;//此時的新根就是subR_root->_parent = ppNode;}else//parent不是根{//將新的根連接到ppNodeif (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}}private:Node* _root = nullptr;
};
Mymap.h
#pragma oncenamespace cp
{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 _m.Begin();}iterator end(){return _m.End();}const_iterator begin() const{return _m.Begin();}const_iterator end() const{return _m.End();}iterator find(const K& key){return _m.Find(key);}pair<iterator, bool> insert(const pair<K, V>& kv){return _m.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _m.Insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapKeyofT> _m;};
}
Myset.h
#pragma oncenamespace cp
{template<class K>class set{struct SetKofT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKofT>::Iterator iterator;typedef typename RBTree<K, const K, SetKofT>::ConstIterator const_iterator;iterator begin(){return _s.Begin();}iterator end(){return _s.End();}const_iterator begin() const{return _s.Begin();}const_iterator end() const{return _s.End();}iterator find(const K& key){return _s.Find(key);}pair<iterator, bool> insert(const K& key){return _s.Insert(key);}private:RBTree<K, const K, SetKofT> _s;};
}
OK,本期分享就到這里,好兄弟我們下期再見~!
結束語:我們的目標是星辰大海!