模拟实现mapset
模擬實現map&set
文章目錄
- 模擬實現map&set
- 1. map的特性
- 2. map的模板參數說明
- 3. map的構造
- 4. map的迭代器
- 5. map的容量與元素訪問
- 6. map中元素的修改
- 7. map的模擬實現
- 8. set的模擬實現
1. map的特性
1. map是關聯容器,它按照特定的次序(按照key來比較)存儲由鍵值key和值value組合而成的元素。
2. 在map中,鍵值key通常用于排序和惟一地標識元素,而值value中存儲與此鍵值key關聯的內容。鍵值key和值value的類型可能不同,并且在map的內部,key與value通過成員類型value_type綁定在一起,為其取別名稱為pair:
typedef pair value_type;
3. 在內部,map中的元素總是按照鍵值key進行比較排序的。
4. map中通過鍵值訪問單個元素的速度通常比unordered_map容器慢,但map允許根據順序對元素進行直接迭代(即對map中的元素進行迭代時,可以得到一個有序的序列)。
5. map支持下標訪問符,即在[]中放入key,就可以找到與key對應的value。
6. map通常被實現為二叉搜索樹(更準確的說:平衡二叉搜索樹(紅黑樹))
2. map的模板參數說明
- key: 鍵值對中key的類型
- T: 鍵值對中value的類型
- Compare: 比較器的類型,map中的元素是按照key來比較的,缺省情況下按照小于來比較,一般情況下(內置類型元素)該參數不需要傳遞,如果無法比較時(自定義類型),需要用戶自己顯式傳遞比較規則(一般情況下按照函數指針或者仿函數來傳遞)
- Alloc:通過空間配置器來申請底層空間,不需要用戶傳遞,除非用戶不想使用標準庫提供的空間配置器
注意:在使用map時,需要包含頭文件。
3. map的構造
| map() | 構造一個空的map |
4. map的迭代器
5. map的容量與元素訪問
| bool empty ( ) const | 檢測map中的元素是否為空,是返回true,否則返回false |
| size_type size() const | 返回map中有效元素的個數 |
| mapped_type& operator[] (constkey_type& k) | 返回去key對應的value |
注意當key不存在時,mapped_type& operator[] (constkey_type& k)相當于用默認value與key構造鍵值對然后插入,返回該默認value,at()函數直接拋異常
6. map中元素的修改
| pair<iterator,bool> insert (const value_type& x ) | 在map中插入鍵值對x,注意x是一個鍵值對,返回值也是鍵值對:iterator代表新插入元素的位置,bool代表釋放插入成功 |
| void erase ( iterator position ) | 刪除position位置上的元素 |
| size_type erase ( const key_type& x) | 刪除鍵值為x的元素 |
| void erase ( iterator first,iterator last ) | 刪除[first, last)區間中的元素 |
| void swap (map<Key,T,Compare,Allocator>&mp) | 交換兩個map中的元素 |
| void clear ( ) | 將map中的元素清空 |
| iterator find ( const key_type& x) | 在map中插入key為x的元素,找到返回該元素的位置的迭代器,否則返回end |
| const_iterator find ( const key_type& x ) const | 在map中插入key為x的元素,找到返回該元素的位置的const迭代器,否則返回cend |
| size_type count ( const key_type& x ) const | 返回key為x的鍵值在map中的個數,注意map中key是唯一的,因此該函數的返回值要么為0,要么為1,因此也可以用該函數來檢測一個key是否在map中 |
【總結】
set/multimap/multiset的接口和map基本類似,主要的區別在于元素能否重復,是否可以使用operator[]操作
7. map的模擬實現
map的底層使用的是紅黑樹,所以先封裝一個紅黑樹
紅黑樹
要實現map還需要在前文的基礎上進行填充一些操作:
1. 紅黑樹的迭代器:
迭代器的好處是可以方便遍歷,是數據結構的底層實現與用戶透明。如果想要給紅黑樹增加迭代器,需要考慮以前問題:
begin()與end()
- STL明確規定,begin()與end()代表的是一段前閉后開的區間,而對紅黑樹進行中序遍歷后,可以得到一 個有序的序列,
- 因此:begin()可以放在紅黑樹中最小節點(即最左側節點)的位置,end()放在最大節點(最右側節點)的下一個位置,關鍵是最大節點的下一個位置在哪塊?
- 能否給成nullptr呢?答案是行不通的,因為對end()位置的迭代器進行–操作,必須要能找最后一個元素,此處就不行,因此最好的方式是將end()放在頭結點的位置
2. operator++()與operator–()
// 找迭代器的下一個節點,下一個節點肯定比其大 void Increasement() {//分兩種情況討論:_pNode的右子樹存在和不存在// 右子樹存在if(_pNode->_pRight){// 右子樹中最小的節點,即右子樹中最左側節點_pNode = _pNode->_pRight;while(_pNode->_pLeft)_pNode = _pNode->_pLeft;}else{// 右子樹不存在,向上查找,直到_pNode != pParent->rightPNode pParent = _pNode->_pParent;while(pParent->_pRight == _pNode){_pNode = pParent;pParent = _pNode->_pParent;}// 特殊情況:根節點沒有右子樹if(_pNode->_pRight != pParent)_pNode = pParent;} }// 獲取迭代器指向節點的前一個節點 void Decreasement() {//分三種情況討論:_pNode 在head的位置,_pNode 左子樹存在,_pNode 左子樹不存在// 1. _pNode 在head的位置,--應該將_pNode放在紅黑樹中最大節點的位置if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)_pNode = _pNode->_pRight;else if(_pNode->_pLeft){// 2. _pNode的左子樹存在,在左子樹中找最大的節點,即左子樹中最右側節點_pNode = _pNode->_pLeft;while(_pNode->_pRight)_pNode = _pNode->_pRight;}else{// _pNode的左子樹不存在,只能向上找PNode pParent = _pNode->_pParent;while(_pNode == pParent->_pLeft){_pNode = pParent;pParent = _pNode->_pParent;}_pNode = pParent;} }3. 2 改造紅黑樹
// 因為關聯式容器中存儲的是<key, value>的鍵值對,因此 // k為key的類型, // ValueType: 如果是map,則為pair<K, V>; 如果是set,則為k // KeyOfValue: 通過value來獲取key的一個仿函數類 template<class K, class ValueType, class KeyOfValue>class RBTree{typedef RBTreeNode<ValueType> Node;typedef Node* PNode;public:typedef RBTreeIterator<ValueType, ValueType*, ValueType&> Iterator;public:RBTree();~RBTree() / // IteratorIterator Begin(){ return Iterator(_pHead->_pLeft);}Iterator End(){ return Iterator(_pHead);} // // Modifypair<Iterator, bool> Insert(const ValueType& data){// 插入節點并進行調整// 參考上文...return make_pair(Iterator(pNewNode), true);}// 將紅黑樹中的節點清空void Clear();Iterator Find(const K& key); // // capacitysize_t Size()const;bool Empty()const;// ……private:PNode _pHead;size_t _size; // 紅黑樹中有效節點的個數 };RBTree.hpp
#pragma once #include<algorithm>enum Color {RED,BLACK };template<class ValueType> struct RBTreeNode {RBTreeNode(const ValueType& data = ValueType(), Color color = RED):_pLeft(nullptr),_pRight(nullptr),_pParent(nullptr),_data(data),_color(color){}RBTreeNode<ValueType>* _pLeft;RBTreeNode<ValueType>* _pRight;RBTreeNode<ValueType>* _pParent;ValueType _data;Color _color; };template<class T> class Iterator { public:typedef RBTreeNode<T> Node;typedef Iterator<T> Self;Iterator(Node* pNode=nullptr):_pNode(pNode){}T& operator*() {return _pNode->_data;}T* operator->() {return &(_pNode->_data);}Self& operator++() {Next();return *this;}Self operator++(int) {Self tmp(_pNode);Next();return tmp;}bool operator==(Self& t){return _pNode == t._pNode;}bool operator!=(Self& t){return _pNode != t._pNode;}Self& operator--() {Prev();return *this;}Self operator--(int) {Self tmp(_pNode);Prev();return tmp;} private:void Next() {//如果有右子樹if (_pNode->_pRight) {_pNode = _pNode->_pRight;while (_pNode->_pLeft)_pNode = _pNode->_pLeft;return;}Node* pParent = _pNode->_pParent;while (pParent->_pRight == _pNode) {_pNode = pParent;pParent = _pNode->_pParent;}//根節點沒有右子樹,并且迭代器剛好在根節點位置if (_pNode->_pRight != pParent)_pNode = pParent;}void Prev() {//1._pNode在head位置(即end()位置),應該將_pNode放在最大結點處if (_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)_pNode = _pNode->_pRight;//2. 如果有左子樹else if (_pNode->_pLeft) {_pNode = _pNode->_pLeft;while (_pNode->_pRight)_pNode = _pNode->_pRight;}else {Node* pParent = _pNode->_pParent;while (pParent->_pLeft == _pNode) {_pNode = pParent;pParent = _pNode->_pParent;}_pNode = pParent;}}Node* _pNode; };template<class T,class KorV> class RBTree { public:typedef RBTreeNode<T> Node;typedef Node* PNode;typedef RBTree<T, KorV> Self;typedef Iterator<T> iterator; public:RBTree():_pHead(new Node),_size(0){_pHead->_pLeft = _pHead;_pHead->_pRight = _pHead;}~RBTree() {if (_pHead->_pParent)del(_pHead->_pParent);delete _pHead;}std::pair<iterator,bool> Insert(const T& data) {PNode& pRoot = GetRoot(); //獲取根節點PNode newPtr = nullptr;if (nullptr == pRoot) { //如果紅黑樹為空newPtr = pRoot = new Node(data, BLACK);pRoot->_pParent = _pHead;}else {PNode pParent = nullptr;PNode pCur = pRoot;//插入節點while (pCur) {pParent = pCur;if (KorV()(data) < KorV()(pCur->_data))pCur = pCur->_pLeft;else if (KorV()(data) > KorV()(pCur->_data))pCur = pCur->_pRight;elsereturn std::make_pair(iterator(), false);}newPtr = pCur = new Node(data);if (KorV()(data) < KorV()(pParent->_data)) {pParent->_pLeft = pCur;pCur->_pParent = pParent;}else {pParent->_pRight = pCur;pCur->_pParent = pParent;}//檢測新節點插入后。紅黑樹的性質是否遭到破壞while (pParent != _pHead && RED == pParent->_color) {PNode pGrand = pParent->_pParent;//pParent在pGrand左側的情況if (pParent == pGrand->_pLeft) {PNode unclue = pGrand->_pRight;//情況一if (unclue&&RED == unclue->_color) {pParent->_color = BLACK;unclue->_color = BLACK;pGrand->_color = RED;pCur = pGrand;pParent = pCur->_pParent;}else {//情況三if (pParent->_pRight == pCur) {RotateLeft(pParent);std::swap(pParent, pCur);}//情況二RotateRight(pGrand);pParent->_color = BLACK;pGrand->_color = RED;}}//pParent在pGrand右側的情況else {PNode unclue = pGrand->_pLeft;//情況一if (unclue&&RED == unclue->_color) {pParent->_color = BLACK;unclue->_color = BLACK;pGrand->_color = RED;pCur = pGrand;pParent = pCur->_pParent;}else {//情況三if (pParent->_pLeft == pCur) {RotateRight(pParent);std::swap(pParent, pCur);}//情況二RotateLeft(pGrand);pParent->_color = BLACK;pGrand->_color = RED;}}}}//根節點的顏色可能被修改,將其改回黑色pRoot->_color = BLACK;//更新頭結點的左右孩子_pHead->_pLeft = LeftMost();_pHead->_pRight = RightMost();++_size;return std::make_pair(iterator(newPtr), true);}void Inorder(){_InOrder(GetRoot());std::cout << std::endl;}bool IsValidRBTree(){PNode pRoot = GetRoot();// 空樹也是紅黑樹if (nullptr == pRoot)return true;// 檢測根節點是否滿足情況if (BLACK != pRoot->_color){std::cout << "違反紅黑樹性質二:根節點必須為黑色" << std::endl;return false;}// 獲取任意一條路徑中黑色節點的個數size_t blackCount = 0;PNode pCur = pRoot;while (pCur){if (BLACK == pCur->_color)blackCount++;pCur = pCur->_pLeft;}// 檢測是否滿足紅黑樹的性質,k用來記錄路徑中黑色節點的個數size_t k = 0;return _IsValidRBTree(pRoot, k, blackCount);}iterator find(const T& data)const {PNode ptr = GetRoot();while (ptr) {if (KorV()(data) == KorV()(ptr->_data))return iterator(ptr);else if (KorV()(data) < KorV()(ptr->_data))ptr = ptr->_pLeft;elseptr = ptr->_pRight;}return end();}size_t size()const {return _size;}bool empty()const {return _size == 0;}iterator begin() {return iterator(_pHead->_pLeft);}iterator end() {return iterator(_pHead);} private:void _InOrder(Node* pRoot){if (pRoot){_InOrder(pRoot->_pLeft);std::cout << pRoot->_data << " ";_InOrder(pRoot->_pRight);}}void del(PNode ptr) {if (ptr->_pLeft)del(ptr->_pLeft);if (ptr->_pRight)del(ptr->_pRight);delete ptr;}bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount) {//走到null之后,判斷k和black是否相等if (nullptr == pRoot){if (k != blackCount){std::cout << "違反性質四:每條路徑中黑色節點的個數必須相同" << std::endl;return false;}return true;}// 統計黑色節點的個數if (BLACK == pRoot->_color)k++;// 檢測當前節點與其雙親是否都為紅色PNode pParent = pRoot->_pParent;if (pParent && RED == pParent->_color && RED == pRoot->_color){std::cout << "違反性質三:沒有連在一起的紅色節點" << std::endl;return false;}return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&_IsValidRBTree(pRoot->_pRight, k, blackCount);}PNode& GetRoot() {return _pHead->_pParent;}PNode LeftMost() {PNode ptr = GetRoot();if (!ptr)return _pHead;while (ptr->_pLeft)ptr = ptr->_pLeft;return ptr;}PNode RightMost() {PNode ptr = GetRoot();if (!ptr)return _pHead;while (ptr->_pRight)ptr = ptr->_pRight;return ptr;}void RotateLeft(PNode pParent) {PNode pPParent = pParent->_pParent;PNode pRight = pParent->_pRight;pParent->_pRight = pRight->_pLeft;if (pRight->_pLeft)pRight->_pLeft->_pParent = pParent;pRight->_pLeft = pParent;pParent->_pParent = pRight;//當pPParent不存在時if (pPParent==_pHead) {//設置pRight為根節點_pHead->_pParent = pRight;pRight->_pParent = _pHead;}else {if (pPParent->_pLeft == pParent) {pPParent->_pLeft = pRight;pRight->_pParent = pPParent;}else {pPParent->_pRight = pRight;pRight->_pParent = pPParent;}}}void RotateRight(PNode pParent) {PNode pPParent = pParent->_pParent;PNode pLeft = pParent->_pLeft;pParent->_pLeft = pLeft->_pRight;if (pLeft->_pRight)pLeft->_pRight->_pParent = pParent;pLeft->_pRight = pParent;pParent->_pParent = pLeft;//當pPParent不存在時if (pPParent == _pHead) {//設置pLeft為根節點_pHead->_pParent = pLeft;pLeft->_pParent = _pHead;}else {if (pPParent->_pLeft == pParent) {pPParent->_pLeft = pLeft;pLeft->_pParent = pPParent;}else {pPParent->_pRight = pLeft;pLeft->_pParent = pPParent;}}} private:PNode _pHead; //頭結點(根節點的父親節點)size_t _size; };map.hpp
#include"RBTree.hpp"template<class K,class V> class Map {typedef std::pair<K, V> ValueType;struct KorV {const K& operator()(const ValueType& v)const{return v.first;}};typename typedef RBTree<ValueType, KorV>::iterator iterator; public:Map() {}iterator begin() {return _tree.begin();}iterator end() {return _tree.end();}size_t size()const {return _tree.size();}bool empty()const {return _tree.empty();}std::pair<iterator, bool> insert(const ValueType& data) {return _tree.Insert(data);}iterator find(const K& key)const{return _tree.find(ValueType(key, V()));}V& operator[](const K& k) {return (*(insert(ValueType(k, V())).first)).second;} private:RBTree<ValueType, KorV> _tree; };8. set的模擬實現
set.hpp
#include"RBTree.hpp"template<class K> class Set {typedef K ValueType;struct KorV {const K& operator()(const ValueType& data) {return data;}}; public:typename typedef RBTree<ValueType, KorV>::iterator iterator; public:Set(){}iterator begin() {return _tree.begin();}iterator end() {return _tree.end();}std::pair<iterator, bool> insert(const ValueType& data) {return _tree.Insert(data);}size_t size()const {return _tree.size();}bool empty()const {return _tree.empty();}iterator find(const K& key) {return _tree.find(key);} private:RBTree<ValueType, KorV> _tree; };總結
以上是生活随笔為你收集整理的模拟实现mapset的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 模拟实现unordered_mapuno
- 下一篇: 位图布隆过滤器海量数据处理