今天小編給大家分享一下C++ map與set封裝如何實現的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
set 參數只有 key,但是map除了key還有value。我們還是需要KV模型的紅黑樹的:
#pragma once #include <iostream> #include <assert.h> #include <time.h> using namespace std; enum Color { 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; Color _col; RBTreeNode(const pair<K,V>& kv) :_kv(kv) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) ,_col(RED) {} }; template<class K,class V> class RBTree { typedef RBTreeNode<K, V> Node; public: bool Insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); _root->_col = BLACK; return true; } Node* parent = nullptr; Node* cur = _root; while (cur) { if (cur->_kv.first < kv.first) { parent = cur; cur = cur->_right; } else if (cur->_kv.first > kv.first) { parent = cur; cur = cur->_left; } else { return false; } } cur = new Node(kv); cur->_col = RED; if (parent->_kv.first < kv.first) { parent->_right = cur; cur->_parent = parent; } else { parent->_left = cur; cur->_parent = parent; } while (parent && parent->_col == RED) { Node* grandfater = parent->_parent; if (parent == grandfater->_left) { Node* uncle = grandfater->_right; //情況一:u存在且為紅 if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfater->_col = RED; //向上調整 cur = grandfater; parent = cur->_parent; } else { //情況2 if (cur == parent->_left) { RotateR(grandfater); parent->_col = BLACK; grandfater->_col = RED; } //情況3 else { // g // p // c RotateL(parent); RotateR(grandfater); cur->_col = BLACK; grandfater->_col = RED; } break; } } else//parent==grandfater->_right { Node* uncle = grandfater->_left; //情況1:u存在且為紅色 if (uncle && uncle->_col == RED) { uncle->_col = parent->_col = BLACK; grandfater->_col = RED; //向上調整 cur = grandfater; parent = cur->_parent; } else { //情況2:u不存在/u存在為黑色 //g // p // c if (cur == parent->_right) { RotateL(grandfater); grandfater->_col = RED; parent->_col = BLACK; } //情況3 // g // p // c else { RotateR(parent); RotateL(grandfater); cur->_col = BLACK; grandfater->_col = RED; } break; } } } //根變黑 _root->_col = BLACK; return true; } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) subRL->_parent = parent; Node* ppNode = parent->_parent; subR->_left = parent; parent->_parent = subR; if (ppNode == nullptr) { _root = subR; _root->_parent = nullptr; } else { if (ppNode->_left == parent) { ppNode->_left = subR; } else { ppNode->_right = subR; } subR->_parent = ppNode; } } void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; parent->_left = subLR; if (subLR) subLR->_parent = parent; Node* ppNode = parent->_parent; parent->_parent = subL; subL->_right = parent; if (ppNode == nullptr) { _root = subL; _root->_parent = nullptr; } else { if (ppNode->_left == parent) { ppNode->_left = subL; } else { ppNode->_right = subL; } subL->_parent = ppNode; } } void InOrder() { _InOrder(_root); } 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,int blackNum,int ref) { if (root == nullptr) { //cout << blackNum << endl; if (blackNum != ref) { 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,ref) && Check(root->_right,blackNum,ref); } bool IsBalance() { if (_root == nullptr) { return true; } if (_root->_col != BLACK) { return false; } int ref = 0; Node* left = _root; while (left) { if (left->_col == BLACK) { ++ref; } left = left->_left; } return Check(_root,0,ref); } private: Node* _root = nullptr; };
翻開源碼一看
RBTree的結構源碼:是KV結構的紅黑樹
RBTree是通過傳入的Value的值來判斷類型,也就是一棵泛型的RBTree,通過不同的實例化,實現出了Map和Set:
對于map:傳key,對于set:傳pair
map的結構簡化源碼:
set的結構簡化源碼:
為了讓我們的紅黑樹能夠識別set與map我們增加一個模板參數T:
template<class K, class T> class RBTree
對于T模板參數可能是鍵值Key,也可能是由Key和Value共同構成的鍵值對。
如果是set容器,那么它傳入底層紅黑樹的模板參數就是Key和Key:
template<class K> class set { private: RBTree<K,K> _t; };
如果是map容器,傳入底層紅黑樹的模板參數就是Key和Key和value的鍵值對:
class map { private: RBTree<K, pair<const K,V>> _t; };
通過上面,我們可以知道,對于set和map的區別:我們只要通過第二個模板參數就能進行區分,那是不是第一個模板參數就沒有意義了呢?
對于insert(const Value&v)來說,需要放入存入的值,確實是這個樣子的,插入的值是value,對于set就是key,對于map就是pair。
但是對于find(const Key&key)來說,查找的參數不是value,找的不是pair而是Key,對于map容器來說就不行了。
**紅黑樹的節點**:set容器:K和T都是鍵值Key; map容器:K是鍵值Key,T由Key和Value構成的鍵值對;但是底層紅黑樹并不知道上層容器到底是map還是set,因此紅黑樹的結點當中直接存儲T就行了,如果是set的時候,結點當中存儲的是鍵值Key;如果是map的時候,結點當中存儲的就是鍵值對,所以紅黑樹的結點定義如下,由T類型來決定紅黑樹存的是key還是pair:
template<class T> //三叉鏈結構 struct RBTreeNode { T _data; RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; Color _col; RBTreeNode(const T& data) :_data(data) , _left(nullptr) , _right(nullptr) , _parent(nullptr) , _col(RED) {} };
這里存在一個問題????:插入的時候data的大小如何去進行比較:我們并不知道是什么類型是key,還是pair的比較,而我們剛開始kv結構就直接用kv.first去比較了。
對于set是Key
,可以比較
對于map是pair
,那我們要取其中的first來比較,但是pair的大小并不是直接按照first去進行比較的,而我們只需要按照first去進行比較
由于底層的紅黑樹不知道傳的是map還是set容器,當需要進行兩個結點鍵值的比較時,底層紅黑樹傳入的仿函數來獲取鍵值Key,進行兩個結點鍵值的比較:這個時候我們就需要仿函數了,如果是set那就是用于返回T當中的鍵值Key,如果是map那就是用于返回pair的first:
仿函數/函數對象也是類,是一個類對象。仿函數要重載operator()。
namespace HWC { template<class K,class V> class map { struct MapKeyOfT { const K& operator()(const pair<const K, V>& kv) { return kv.first; } }; public: private: RBTree<K, pair<const K,V>,MapKeyOfT> _t; };
namespace HWC { template<class K> class set { struct SetKeyOfT { const K& operator()(const K& key) { return key; } }; private: RBTree<K,K,SetKeyOfT> _t; };
博主畫了個圖更加容易進行比對
查找過程,此時就可以套上我們所寫的仿函數對象去進行數據的大小比較了:
KeyOfT kot;//仿函數對象 Node* parent = nullptr; Node* cur = _root; 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 false; } }
紅黑樹的正向迭代器是對結點指針進行了封裝,所以這里的正向迭代器就只有一個成員變量:結點的指針,并沒有什么其他的地方,迭代器的定義:
template<class T,class Ref,class Ptr> struct __RBTreeIterator { typedef RBTreeNode<T> Node; typedef __RBTreeIterator<T,Ref,Ptr> Self; typedef __RBTreeIterator<T, T&, T*> iterator; Node* _node; __RBTreeIterator(Node*node) :_node(node) {} //普通迭代器的時候,它是拷貝構造 //const迭代器的時候,它是構造,支持用普通迭代器構造const迭代器 __RBTreeIterator(const iterator& s) :_node(s._node) {} }
*:解引用操作,返回對應結點數據的引用:
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; }
這里的迭代器重點是迭代器的++:
一個結點的正向迭代器進行++
操作后,根據紅黑樹中序(左、根、右)找到當前結點的下一個結點,中序的第一個節點是最左,迭代器的++
怎么去找:
如果節點的右子樹不為空,++
就是找右子樹的最左節點
如果節點的右子樹為空,++
就是找祖先(孩子是父親的左的那個祖先)
代碼實現:
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 = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; }
迭代器的--
對于–,如果是根,–就是左子樹,找到左子樹最大的那一個(最右節點)
如果節點的左子樹不為空,--
找左子樹最右的節點
如果節點的左子樹為空,--
找祖先(孩子是父親的右的祖先)
代碼實現:
Self& operator--() { if (_node->_left) { Node* max = _node->_left; while (max->_right) { max = max->_right; } _node = max; } else { Node* cur = _node; Node* parent = cur->_parent; while (parent&&cur==parent->_left) { cur = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; }
不要忘記迭代器的兩個核心成員:begin()與end()
begin()
:返回中序(左、根、右)第一個結點的正向迭代器,即最左節點,返回的是最左節點,直接找最左節點即可
end()
:返回中序(左、根、右)最后一個結點下一個位置的正向迭代器,這里直接用空指針
template<class K, class T,class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; public: typedef __RBTreeIterator<T> iterator; iterator begin() { Node* left = _root; while (left && left->_left) { left = left->_left; } return iterator(left); } iterator end() { return iterator(nullptr); } }
通過前面底層紅黑樹的接口進行套用即可實現set的實現:
值得注意的是????:typename:沒有實例化的模板,區分不了是靜態變量還是類型,typename告訴編譯器是類型
#pragma once #include "RBTree.h" namespace hwc { template <class K> class set { struct SetKeyOfT { const K& operator()(const K& key) { return key; } }; public: //typename:沒有實例化的模板,區分不了是靜態變量還是類型,typename告訴編譯器是類型 typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;//key不可以修改 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) { //底層紅黑樹的iterator是普通迭代器 pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key); return pair<iterator, bool>(ret.first, ret.second);//用普通迭代器構造const迭代器 } private: RBTree<K, K,SetKeyOfT> _t; }; void test_set() { int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; set<int> s; for (auto e : a) { s.insert(e); } set<int>::iterator it = s.begin(); while (it != s.end()) { cout << *it << " "; ++it; } cout << endl; for (auto e : s) { cout << e << " "; } cout << endl; } }
同樣是套用上底層紅黑樹的接口,不過map的實現有一個很重要的地方,那就是[]的實現
#pragma once #include "RBTree.h" namespace hwc { template<class K,class V> class map { struct MapkeyOfT { const K& operator()(const pair<const K, V>& kv) { return kv.first; } }; public: //typename:沒有實例化的模板,區分不了是靜態變量還是類型,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(); } pair<iterator,bool> insert(const pair<const K, V>& kv) { return _t.Insert(kv); } 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() { int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; map<int, int> m; for (auto e : a) { m.insert(make_pair(e, e)); } map<int, int>::iterator it = m.begin(); while(it!=m.end()) { it->second++; cout << it->first << ":" << it->second << endl; ++it; } cout << endl; map<string, int> countMap; string arr[] = { "蘋果","西瓜","香蕉","蘋果"}; for (auto& e : arr) { countMap[e]++; } for (auto& kv : countMap) { cout << kv.first << ":" << kv.second << endl; } } }
最后,在這里送上源碼:
#pragma once #pragma once #include <iostream> #include <assert.h> #include <time.h> using namespace std; enum Color { RED, BLACK, }; template<class T> struct RBTreeNode { T _data; RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; Color _col; RBTreeNode(const T& data) :_data(data) , _left(nullptr) , _right(nullptr) , _parent(nullptr) , _col(RED) {} }; template<class T,class Ref,class Ptr> struct __RBTreeIterator { typedef RBTreeNode<T> Node; typedef __RBTreeIterator<T,Ref,Ptr> Self; typedef __RBTreeIterator<T, T&, T*> iterator; Node* _node; __RBTreeIterator(Node*node) :_node(node) {} //普通迭代器的時候,它是拷貝構造 //const迭代器的時候,它是構造,支持用普通迭代器構造const迭代器 __RBTreeIterator(const iterator& s) :_node(s._node) {} Ref operator*() { return _node->_data; } Ptr operator->() { return &_node->_data; } 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 = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; } Self& operator--() { if (_node->_left) { Node* max = _node->_left; while (max->_right) { max = max->_right; } _node = max; } else { Node* cur = _node; Node* parent = cur->_parent; while (parent&&cur==parent->_left) { cur = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; } bool operator !=(const Self & s) const { return _node != s._node; } bool operator ==(const Self& s) const { 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*> const_iterator; const_iterator begin() const { Node* left = _root; while (left && left->_left) { left = left->_left; } return const_iterator(left); } const_iterator end() const { return const_iterator(nullptr); } iterator begin() { Node* left = _root; while (left && left->_left) { left = left->_left; } return iterator(left); } iterator end() { return iterator(nullptr); } pair<iterator,bool> Insert(const T& data) { if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK; return make_pair(iterator(_root),true); } KeyOfT kot; Node* parent = nullptr; Node* cur = _root; 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; 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* grandfater = parent->_parent; if (parent == grandfater->_left) { Node* uncle = grandfater->_right; //情況一:u存在且為紅 if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfater->_col = RED; //向上調整 cur = grandfater; parent = cur->_parent; } else { //情況2 if (cur == parent->_left) { RotateR(grandfater); parent->_col = BLACK; grandfater->_col = RED; } //情況3 else { // g // p // c RotateL(parent); RotateR(grandfater); cur->_col = BLACK; grandfater->_col = RED; } break; } } else//parent==grandfater->_right { Node* uncle = grandfater->_left; //情況1:u存在且為紅色 if (uncle && uncle->_col == RED) { uncle->_col = parent->_col = BLACK; grandfater->_col = RED; //向上調整 cur = grandfater; parent = cur->_parent; } else { //情況2:u不存在/u存在為黑色 //g // p // c if (cur == parent->_right) { RotateL(grandfater); grandfater->_col = RED; parent->_col = BLACK; } //情況3 // g // p // c else { RotateR(parent); RotateL(grandfater); cur->_col = BLACK; grandfater->_col = RED; } break; } } } //根變黑 _root->_col = BLACK; return make_pair(iterator(newnode),true); } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) subRL->_parent = parent; Node* ppNode = parent->_parent; subR->_left = parent; parent->_parent = subR; if (ppNode == nullptr) { _root = subR; _root->_parent = nullptr; } else { if (ppNode->_left == parent) { ppNode->_left = subR; } else { ppNode->_right = subR; } subR->_parent = ppNode; } } void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; parent->_left = subLR; if (subLR) subLR->_parent = parent; Node* ppNode = parent->_parent; parent->_parent = subL; subL->_right = parent; if (ppNode == nullptr) { _root = subL; _root->_parent = nullptr; } else { if (ppNode->_left == parent) { ppNode->_left = subL; } else { ppNode->_right = subL; } subL->_parent = ppNode; } } void InOrder() { _InOrder(_root); } 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, int blackNum, int ref) { if (root == nullptr) { //cout << blackNum << endl; if (blackNum != ref) { 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, ref) && Check(root->_right, blackNum, ref); } bool IsBalance() { if (_root == nullptr) { return true; } if (_root->_col != BLACK) { return false; } int ref = 0; Node* left = _root; while (left) { if (left->_col == BLACK) { ++ref; } left = left->_left; } return Check(_root, 0, ref); } private: Node* _root = nullptr; };
以上就是“C++ map與set封裝如何實現”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。