文章目录
- 一.map/set 的封装思路
- 1.封装思路
- 2.红黑树节点调整
- 3.map 和 set 的定义
- 4.仿函数 KeyOfValue
- 5.map/set 的插入
- 二.map/set 迭代器实现
- 1.迭代器的定义
- 2.解引用运算符重载
- 3.成员访问运算符重载
- 4.(不)等于运算符重载
- 5.begin() 与 end()
- 6.++ 运算符重载
- 7.-- 运算符重载
- 8.[ ]下标访问运算符重载
- 三.源码
- 1.RBTree
- 2.map.h
- 3.set.h
预备知识:
一.map/set 的封装思路
在实现了红黑树的部分功能后,我们可以便可以将红黑树作为底层结构来封装map
和set
,其中map
是 K-Value
模型 ,而 set
是 Key
模型。
我们接下来将使用模板、仿函数和一棵红黑树实现 map
和set
。
1.封装思路
因为 map
存储的是pair
,而 set
存储的是 Key
,所以其解决的根本方向就是:
-
如果是
map
,红黑树中就按照pair
的K
进行比较,从而插入; -
如果是
set
,红黑树中就按照Key
值进行比较,从而插入。
让 map / set
主动传出待比较的数据,红黑树只用根据数据间关系进行插入即可,不用在乎待比较的数据是何种结构。
2.红黑树节点调整
上文我们实现的红黑树是按照键值对的方式进行存储的,而接下来我们要同时封装 map/set
,故不能直接定死存储的结构,所以我们在此进行修改。
将原来的kv
模型改为data
模型,data
即是比较的数据内容。
注意,将 Kv
模型改为 data
后,插入与查找中比较的代码都要进行更新,稍后会讲解。
3.map 和 set 的定义
map
和 set
底层都使用的红黑树,所以我们 map/set
的功能就是调用红黑树的成员函数即可。
template<class K, class V>
class Map
{
private:RBTree<K, pair<K, V>> _t;
};template<class K>
class Set
{
private:RBTree<K,K> _t;
};
因为Map
有两个模板参数,而 Set
只有一个模板参数。所以当我们使用的一个红黑树实现时,要进行匹配处理。即使 Set
是一个模板参数,在调用红黑树时也要传入两个模板参数。因为第一个模板参数是匹配 Map
满足红黑树的两个模板参数,而第二个模板参数是为了让底层红黑树拿到比较的数据。
为什么Map
除了传入pair
外,第一个参数直接传入 K
,为什么不能省略?
因为Find
的存在,map
中Find
函数是直接按pair
中的 K
进行查找的,所以要额外设置该参数。
4.仿函数 KeyOfValue
接下来我们就要将数据取出供红黑树比较了,如果是 map
,就按pair
中的 K
去比较,如果是 set
,就按Key
比较。
为此我们可以在 map
和 set
内部定义一个仿函数将其数据取出。
template<class K, class V>
class Map
{//Map-keyofvalue 仿函数struct MapKeyOfvalue{const K& operator()(const std::pair<K, V>& kv){return kv.first;}};
private:RBTree<K, pair<K, V>> _t;
};template<class K>
class Set
{//Set-keyofvalue 仿函数struct SetKeyOfvalue{const K& operator()(const K& key){return key;}};
private:RBTree<K,K> _t;
};
然后我们将其仿函数也作为模板,传入红黑树中,对应的,红黑树要添加一个模板参数来接收该仿函数。
改动代码如下:
改动这些之后,我们便要将红黑树中比较数据大小的地方进行修改
用仿函数将数据取出,然后进行比较:
//根据模板参数创建仿函数
KeyOfvalue kovalue;
if (!_root)
{_root = new Node(data);_root->_col = BLACK;return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{//比较处————进行改动if (kovalue(cur->_data) > kovalue(data)){parent = cur;cur = cur->_left;}//比较处————进行改动else if (kovalue(cur->_data) < kovalue(data)){parent = cur;cur = cur->_right;}else{return false;}
}
//创建新节点,使用data进行构造
cur = new Node(data);
//比较处————进行改动
if (kovalue(parent->_data) > kovalue(data))
{parent->_left = cur;
}
else
{parent->_right = cur;
}
cur->_parent = parent;
这样,红黑树便可以适配 map/set
的插入了。
5.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 迭代器实现
1.迭代器的定义
// 节点数据 引用/const引用 指针/const指针
template <class T,class Ref,class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> self;Node* _node;
public:__RBTreeIterator(Node* node):_node(node){}
}
首先,我们要明确,其实 map/set
只是一层套壳,其中的功能都是由红黑树实现后,再封装到map/set中供我们使用,迭代器也不例外。
2.解引用运算符重载
解引用即返回该节点的存储的数据,主要用于 set
中,返回该数据的引用。
Ref operator*()
{return _node->_data;
}
3.成员访问运算符重载
成员访问操作符即返回该节点的地址,主要用于 map
中,方便访问 pair
中的first
以及second
。
Ptr operator->()
{return &(_node->_data);
}
4.(不)等于运算符重载
bool operator==(const self& s)
{return _node == s._node;
}bool operator!=(const self& s)
{return _node != s._node;
}
5.begin() 与 end()
迭代器常用成员函数begin()
与end()
,其中begin()
对应红黑树的最左节点,end()
对应最后一个节点的下一个节点,即nullptr
(为了简化,并未设置哨兵节点实现将其完美实现)
iterator begin()
{Node* left = _root;while (left && left->_left){left = left->_left;}return iterator(left);
}iterator end()
{return iterator(nullptr);
}
如果 map/set
中想使用红黑树中的迭代器,我们需要在 map/set
中进行声明。
声明如下:
如果想取一个类模板中的一个类型,要使用 typedname
进行声明。
告诉编译器这是一个类型,并不是一个静态变量
//如果想取一个类模板中的一个类型,要使用 typedname 进行声明。
//告诉编译器这是一个类型,并不是一个静态变量
typedef typename RBTree<K, pair<K, V>, MapKeyOfvalue>::iterator iterator;
6.++ 运算符重载
首先我们需要明确,迭代器++
是让当前迭代器指向红黑树中序遍历的下一个节点。
以下图的35节点为例。
- 当迭代器指向 35 时,进行 ++,指向右子树最左节点,即 40。
- 当迭代器指向 40 时,进行 ++,右子树为空,指向父节点,即 45。
- 当迭代器指向 45 时,进行 ++,指向右子树最左节点,即 48。
- 当迭代器指向 48 时,进行 ++,指向未遍历的父节点,即 50。
分析上面的情况,发现迭代器 ++ 始终围绕着右子树是否存在进行。
现在我们将其抽象化,分析其规律:
- 右子树不为空,进行
++
则是指向右子树中序的第一个(最左节点)。 - 右子树为空,
++
找孩子不是父亲右节点的祖先。
self& operator++()
{//如果右子树存在if (_node->_right){Node* left = _node->_right;//则寻找右子树的最左节点while (left->_left){left = left->_left;}_node = left;}//如果右子树不存在else{//找孩子不是父亲右节点的节点Node* parent = _node->_parent;Node* cur = _node;=while (cur == parent->_right){cur = cur->_parent;parent = parent->_parent;//防止最后一个节点寻找祖先导致程序崩溃if (parent == nullptr){break;}}_node = parent;}return *this;
}
需要注意,当 ++
到最后一个节点的时候。有可能在寻找非父亲右节点的祖先时,父节点一路走到 nullptr
的情况,如图:
所以在每次 parent
更新时都进行一次判断,即可。
7.-- 运算符重载
有了前面++
的模拟实现,实现 --
就是反着遍历即可。
- 左子树不为空,进行
--
则是指向左子树中序的最后一个(最右节点)。 - 左子树为空,
--
找孩子不是父亲左节点的祖先。
self& operator--()
{//如果左子树存在if (_node->left){//找左子树的最右节点Node* right = _node->_left;while (right->_right){right = right->_right;}_node = rihgt;}//如果左子树不存在else{//找孩子不是父亲左节点的节点Node* parent = _node->parent;Node* cur = _node;while (parent->_left == cur){cur = cur->_parent;parent = parent->_parent;if (parent == nullptr){break;}}_node = parent;}return *this;
}
8.[ ]下标访问运算符重载
我们来看 map
的[]
下标访问操作符,其中 []
返回的是mapped_type(pair)
类型。
再看 set
中的insert
也是返回 pair
,虽然很反常,但是官方库中确实是这样书写的。
因为 set
没有[]
运算符重载,所以 set
中不必提供该函数,只用在 map
中提供即可。
首先,我们向 map
中 insert
数据 pair
;pair
的第一个参数为用户传入的 key
值,第二个参数则是用户声明的第二个模板参数的默认构造函数(如果是 int
,则调用 int
的构造函数,如果是string
,则默认构造 string
)。
pair<iterator, bool> result = insert(make_pair(key, V()));
然后我们返回迭代器指向的 pair
数据中的second
。
//result.first取出迭代器,使用->运算符重载取出data地址,访问second并返回
return result.first->second;
V& operator[](const K& key)
{pair<iterator, bool> result = insert(make_pair(key, V()));//如果存在,则插入失败//如果不存在,则插入数据//无论是否存在,都返回 second;return result.first->second;
}
三.源码
1.RBTree
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){}
};template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}// 1、typedef __RBTreeIterator<T, T&, T*> itertaor; 拷贝构造// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;// 支持普通迭代器构造const迭代器的构造函数__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it):_node(it._node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_right){// 1、右不为空,下一个就是右子树的最左节点Node* subLeft = _node->_right;while (subLeft->_left){subLeft = subLeft->_left;}_node = subLeft;}else{// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node->_left){// 1、左不为空,找左子树最右节点Node* subRight = _node->_left;while (subRight->_right){subRight = subRight->_right;}_node = subRight;}else{// 2、左为空,孩子是父亲的右的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};// 仿函数
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}
public:typedef __RBTreeIterator<T, T&, T*> itertaor;typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;itertaor begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return itertaor(cur);}itertaor end(){return itertaor(nullptr);}const_itertaor begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_itertaor(cur);}const_itertaor end() const{return const_itertaor(nullptr);}Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return cur;}}return nullptr;}pair<itertaor, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(itertaor(_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(itertaor(cur), false);}}cur = new Node(data);Node* newnode = cur;if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在/u存在且为黑,旋转+变色{// g// p u// c if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// p u// cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED;}break;}}else // (grandfather->_right == parent){// g// u p// cNode* uncle = grandfather->_left;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在/u存在且为黑,旋转+变色{// g// u p// cif (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(itertaor(newnode), true);;}bool IsBalance(){if (_root && _root->_col == RED){cout << "根节点颜色是红色" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}// 连续红色节点return _Check(_root, 0, benchmark);}int Height(){return _Height(_root);}private:void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}int _Height(Node* root){if (root == NULL)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}bool _Check(Node* root, int blackNum, int benchmark){if (root == nullptr){if (benchmark != blackNum){cout << "某条路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return _Check(root->_left, blackNum, benchmark)&& _Check(root->_right, blackNum, benchmark);}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;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}private:Node* _root = nullptr;
};
2.map.h
template<class K, class V>
class map
{struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};
public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::itertaor iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}
private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};void test_map1()
{map<string, string> dict;dict.insert(make_pair("sort", ""));dict.insert(make_pair("string", "ַ"));dict.insert(make_pair("count", ""));dict.insert(make_pair("string", "(ַ)")); // ʧmap<string, string>::iterator it = dict.begin();while (it != dict.end()){cout << it->first << ":" << it->second << endl;/*it->first = "1111";it->second = "111";*/++it;}cout << endl;for (auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}cout << endl;
};
3.set.h
template<class K>
class set
{struct SetKeyOfT{const K& operator()(const K& key){return key;}};
public:typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor iterator;typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}
private:RBTree<K, K, SetKeyOfT> _t;
};