文章目录
- [一.map/set 的封装思路](#一.map/set 的封装思路)
- [二.map/set 迭代器实现](#二.map/set 迭代器实现)
-
- 1.迭代器的定义
- 2.解引用运算符重载
- 3.成员访问运算符重载
- 4.(不)等于运算符重载
- [5.begin() 与 end()](#5.begin() 与 end())
- [6.++ 运算符重载](#6.++ 运算符重载)
- [7.-- 运算符重载](#7.-- 运算符重载)
- [8.[ ]下标访问运算符重载](#8.[ ]下标访问运算符重载)
- 三.源码
预备知识:
一.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
的功能就是调用红黑树的成员函数即可。
cpp
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
内部定义一个仿函数将其数据取出。
cpp
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;
};
然后我们将其仿函数也作为模板,传入红黑树中,对应的,红黑树要添加一个模板参数来接收该仿函数。
改动代码如下:
改动这些之后,我们便要将红黑树中比较数据大小的地方进行修改
用仿函数将数据取出,然后进行比较:
cpp
//根据模板参数创建仿函数
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
的插入直接套用红黑树的即可。
代码如下:
cpp
//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.迭代器的定义
cpp
// 节点数据 引用/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
中,返回该数据的引用。
cpp
Ref operator*()
{
return _node->_data;
}
3.成员访问运算符重载
成员访问操作符即返回该节点的地址,主要用于 map
中,方便访问 pair
中的first
以及second
。
cpp
Ptr operator->()
{
return &(_node->_data);
}
4.(不)等于运算符重载
cpp
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
(为了简化,并未设置哨兵节点实现将其完美实现)
cpp
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
进行声明。
告诉编译器这是一个类型,并不是一个静态变量
cpp
//如果想取一个类模板中的一个类型,要使用 typedname 进行声明。
//告诉编译器这是一个类型,并不是一个静态变量
typedef typename RBTree<K, pair<K, V>, MapKeyOfvalue>::iterator iterator;
6.++ 运算符重载
首先我们需要明确,迭代器++
是让当前迭代器指向红黑树中序遍历的下一个节点。
以下图的35节点为例。
- 当迭代器指向 35 时,进行 ++,指向右子树最左节点,即 40。
- 当迭代器指向 40 时,进行 ++,右子树为空,指向父节点,即 45。
- 当迭代器指向 45 时,进行 ++,指向右子树最左节点,即 48。
- 当迭代器指向 48 时,进行 ++,指向未遍历的父节点,即 50。
分析上面的情况,发现迭代器 ++ 始终围绕着右子树是否存在进行。
现在我们将其抽象化,分析其规律:
- 右子树不为空,进行
++
则是指向右子树中序的第一个(最左节点)。 - 右子树为空,
++
找孩子不是父亲右节点的祖先。
cpp
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.-- 运算符重载
有了前面++
的模拟实现,实现 --
就是反着遍历即可。
- 左子树不为空,进行
--
则是指向左子树中序的最后一个(最右节点)。 - 左子树为空,
--
找孩子不是父亲左节点的祖先。
cpp
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
)。
cpp
pair<iterator, bool> result = insert(make_pair(key, V()));
然后我们返回迭代器指向的 pair
数据中的second
。
cpp
//result.first取出迭代器,使用->运算符重载取出data地址,访问second并返回
return result.first->second;
cpp
V& operator[](const K& key)
{
pair<iterator, bool> result = insert(make_pair(key, V()));
//如果存在,则插入失败
//如果不存在,则插入数据
//无论是否存在,都返回 second;
return result.first->second;
}
三.源码
1.RBTree
cpp
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
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// g
// u p
// c
Node* 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
// c
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
RotateR(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
cpp
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
cpp
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;
};