C++實現(xiàn)map和set封裝詳解
前言
map和set的知識我們學(xué)習(xí)了,模擬實現(xiàn)我們已經(jīng)實現(xiàn)了AVL樹和紅黑樹。熟練知識點(diǎn)為map和set的使用提供了前提,手撕AVL樹和紅黑樹讓我了解到map和set的底層如何實現(xiàn),有了知識點(diǎn)和兩樹的模擬鋪墊,那我們該如何封裝map和set呢?
主體
學(xué)習(xí)map和set封裝咱們按照下面的圖解:

map/set底層原理
Map和Set底層是用紅黑樹封裝的,而紅黑樹結(jié)構(gòu)是KV結(jié)構(gòu)。
RBTree是通過傳入的Value的值來判斷類型,也就是一棵泛型的RBTree,通過不同的實例化,實現(xiàn)出了Map和Set,對于map:傳key,對于set:傳pair,如圖:
?
簡化后的map源碼:
?
簡化后的set源碼:
?
我們查看源碼后,因此在封裝我們map和set時,讓我們的紅黑樹增加一個模板參數(shù)T來識別map和set,代碼如下:(此處的代碼是修改了紅黑樹)
template<class K, class T> class RBTree
分析原理:
如果是set容器,那么它傳入底層紅黑樹的模板參數(shù)就是Key和Key:
template<class K> class set { private: RBTree<K,K> _t; };如果是map容器,傳入底層紅黑樹的模板參數(shù)就是Key和Key和value的鍵值對:
class map { private: RBTree<K, pair<const K,V>> _t; };
問題拓展:
通過上面,我們可以知道,對于set和map的區(qū)別:我們只要通過第二個模板參數(shù)就能進(jìn)行區(qū)分那是不是第一個模板參數(shù)就沒有意義了呢?
對于insert(const Value&v)來說,需要放入存入的值,確實是這個樣子的,插入的值是value,對于set就是key,對于map就是pair。但是對于find(const Key&key)來說,查找的參數(shù)不是value,找的不是pair而是Key,對于map容器來說就不行了。
map/set定義
?
map和set封裝都用頭文件來,代碼如下:
set:
template<class K> class set { private: RBTree<K,K> _t; };map:
class map { private: RBTree<K, pair<const K,V>> _t; };
map/set仿函數(shù)
問題闡述:
插入的時候data的大小如何去進(jìn)行比較:我們并不知道是什么類型是key,還是pair的比較,而我們剛開始kv結(jié)構(gòu)就直接用kv.first去比較了。
對于set是Key,可以比較對于map是pair,那我們要取其中的first來比較,但是pair的大小并不是直接按照first去進(jìn)行比較的,而我們只需要按照first去進(jìn)行比較
問題分析:
由于底層的紅黑樹不知道傳的是map還是set容器,當(dāng)需要進(jìn)行兩個結(jié)點(diǎn)鍵值的比較時,底層紅黑樹傳入的仿函數(shù)來獲取鍵值Key,進(jìn)行兩個結(jié)點(diǎn)鍵值的比較:這個時候我們就需要仿函數(shù)了,如果是set那就是用于返回T當(dāng)中的鍵值Key,如果是map那就是用于返回pair的first:
注意事項:
仿函數(shù)/函數(shù)對象也是類,是一個類對象。仿函數(shù)要重載operator()。
代碼實現(xiàn):
map:
namespace lyk { template<class K, class V> class map { struct MapkeyOfT // 仿函數(shù) { const K& operator()(const pair<const K, V>& kv) { return kv.first; } }; }set:
namespace lyk
{
template <class K>
class set // 仿函數(shù),重載operator()
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
}圖示:
?
我們有了仿函數(shù)時,查找函數(shù)就可以用仿函數(shù)來替換比較部分
KeyOfT kot;//仿函數(shù)對象
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;
}
}map/set插入
接下來 map/set 的插入直接套用紅黑樹的即可,代碼如下:
//map的插入,插入pair
bool insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
//set的插入,插入key
bool insert(const K& key)
{
return _t.Insert(key);
}map/set迭代器
迭代器的定義
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)
{}
//普通迭代器的時候,它是拷貝構(gòu)造
//const迭代器的時候,它是構(gòu)造,支持用普通迭代器構(gòu)造const迭代器
__RBTreeIterator(const iterator& s)
:_node(s._node)
{}
}解引用操作
作用:返回對應(yīng)結(jié)點(diǎn)數(shù)據(jù)的引用
Ref operator*()
{
return _node->_data;
}成員訪問操作符
作用:返回結(jié)點(diǎn)數(shù)據(jù)的引用
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;
}begin() 與 end()
begin():返回中序(左、根、右)第一個結(jié)點(diǎn)的正向迭代器,即最左節(jié)點(diǎn),返回的是最左節(jié)點(diǎn),直接找最左節(jié)點(diǎn)即可
end():返回中序(左、根、右)最后一個結(jié)點(diǎn)下一個位置的正向迭代器,這里直接用空指針
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);
}
}迭代器的++
一個結(jié)點(diǎn)的正向迭代器進(jìn)行++操作后,根據(jù)紅黑樹中序(左、根、右)找到當(dāng)前結(jié)點(diǎn)的下一個結(jié)點(diǎn),中序的第一個節(jié)點(diǎn)是最左,迭代器的++怎么去找:
如果節(jié)點(diǎn)的右子樹不為空,++就是找右子樹的最左節(jié)點(diǎn)如果節(jié)點(diǎn)的右子樹為空,++就是找祖先(孩子是父親的左的那個祖先)
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;
}迭代器的--
對于–,如果是根,–就是左子樹,找到左子樹最大的那一個(最右節(jié)點(diǎn))
如果節(jié)點(diǎn)的左子樹不為空,--找左子樹最右的節(jié)點(diǎn)如果節(jié)點(diǎn)的左子樹為空,--找祖先(孩子是父親的右的祖先)
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;
}源代碼及其測試
map代碼
#include"RBTree.h"
namespace lyk
{
template<class K, class V>
class map
{
struct MapkeyOfT // 仿函數(shù)
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
public:
//typename:沒有實例化的模板,區(qū)分不了是靜態(tài)變量還是類型,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_map1() // 測試代碼
{
map<int, int> m;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
m.insert(make_pair(e, e));
}
map<int, int>::iterator it = m.begin();
while (it != m.end())
{
//it->first += 100;
it->second += 100;
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
}
}set代碼
#include"RBTree.h"
namespace lyk
{
template <class K>
class set // 仿函數(shù),重載operator()
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
//typename:沒有實例化的模板,區(qū)分不了是靜態(tài)變量還是類型,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);//用普通迭代器構(gòu)造const迭代器
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
void test_set1() // 測試代碼
{
set<int> s;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
s.insert(e);
}
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
}紅黑樹代碼
#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)
{}
//普通迭代器的時候,它是拷貝構(gòu)造
//const迭代器的時候,它是構(gòu)造,支持用普通迭代器構(gòu)造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;
//向上調(diào)整
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;
//向上調(diào)整
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 << "違反規(guī)則:本條路徑的黑色結(jié)點(diǎn)的數(shù)量根最左路徑不相等" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "違反規(guī)則:出現(xiàn)連續(xù)的紅色結(jié)點(diǎn)" << 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;
};
測試
測試set:

測試map:

到此這篇關(guān)于C++實現(xiàn)map和set封裝詳解的文章就介紹到這了,更多相關(guān)C++ map和set封裝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VC++6.0實現(xiàn)直線掃描轉(zhuǎn)換的圖文教程
這篇文章主要給大家介紹了關(guān)于VC++6.0實現(xiàn)直線掃描轉(zhuǎn)換的相關(guān)資料,文中通過圖文將實現(xiàn)的步驟一步步介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用VC++6.0具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-01-01
C語言可變參數(shù)與內(nèi)存管理超詳細(xì)講解
有時,您可能會碰到這樣的情況,您希望函數(shù)帶有可變數(shù)量的參數(shù),而不是預(yù)定義數(shù)量的參數(shù)。C 語言為這種情況提供了一個解決方案,這篇文章主要介紹了C語言可變參數(shù)與內(nèi)存管理2023-01-01
VS2019中CMake項目如何指定c++語言標(biāo)準(zhǔn)
這篇文章主要介紹了VS2019中CMake項目如何指定c++語言標(biāo)準(zhǔn),需要的朋友可以參考下2020-02-02


?
?
?
?