注:该封装基于前面博客已实现红黑树,map和set封装并不难,主要还是对红黑树的理解
目录
一. 改造红黑树
1. 改变节点的定义,使用更高维度的泛型
2. 红黑树追加迭代器的实现
1. 红黑树迭代器的构造函数和基本框架
2. begin()和end()的实现
3. operator*和operator->的实现
4. operator++和operator--的实现
5. operator!=和operator==的实现
3. 对RBTree类进行改变
1. 改造insert
2. 增加find的实现
4. 改造后的RBTree树代码展示
二. 封装set
三. 封装map
一. 改造红黑树
颜色定义不更改,但是由于我们是使用一颗红黑树同时来作为封装set和map的底层结构,因此对数据类型需要进行处理,这里参考stl源码给出的解决方式是使用更高维度的泛型,因此我们也定义成更高维度的泛型,即数据类型这里我们使用T来接受va类型。
即我们不再使用KV的形式接受数据,而是用T来接受数据,T决定红黑树存什么数据。
这里和节点定义一样,我们使用T来接受数据,T决定红黑树存什么数据,对于set来说RBTree<K, V>传进来的是K,对于map来说RBTree<K, pair<K, V>>传进来的是pair类型。
虽然用T能解决传进来不同的数据类型,但是由于我们需要取K,对于set来说K就是K,但是,对于map来说是pair的第一个参数,所以由于无法确定T是K还是pair类型,无法准确去取出来T中的类型。
根据官方库可以发现再引进来一个模板参数KeyOfT,用来取出T中的类型,而这个KeOfT传给RBTree的,查看set和map后源码后发现KeyOfT其实是一个仿函数,用来提取T中类型的K的,因此我们就知道RBTree应该有三个模板参数,分别是K,T和KeyOfT
1. 改变节点的定义,使用更高维度的泛型
有了以上推论,我们可以对节点定义进行改变
//改变红黑节点的定义,使用更高维度的泛型
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
color _col;
//构造函数
RBTreeNode(const T& data)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_data(data)
,_col(RED)
{}
};
这里使用T来标识数据类型,因为节点可能是K类型的(set传过来的),也可能是pair类型的(map传过来的)
2. 红黑树追加迭代器的实现
由于map和set都需要用到迭代器,但是两者的迭代器实际上都是封装了红黑树的迭代器,因此我们要先实现红黑树的迭代器,这里迭代器的实现和链表极其相似,因此,也是使用三个模板参数,分别代表普通类型,引用类型,指针类型,库里的实现是采用了哨兵卫的头节点的方式,但是这里并不这样做
1. 红黑树迭代器的构造函数和基本框架
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)
{}
operator*()
{}
operator->()
{}
operator++()
{}
operator++(int)
{}
operator--()
{}
operator--(int)
{}
operator!=(const Self& s) const
{}
operator==(const Self& s) const
{}
};
和链表迭代器的实现基本一致,将迭代器的基本功能实现
2. begin()和end()的实现
由于迭代器访问的顺序是按照二叉平衡搜索树的中序的顺序访问的,因此我们也不能坏这个规则。
按中序访问的思路,左子树->根->右子树:
begin应该返回中序的第一个元素,即需要去找二叉平衡搜索树的最左节点:
iterator Begin()
{
Node* subleft = _root;
while (subleft && subleft->_left)
{
subleft = subleft->_left;
}
return iterator(subleft);
}
由于迭代器是自己实现的类,因此应该用迭代器创建对象并返回
end()应该返回的是nullptr,因为最后遍历完成后应该是遍历到最右节点的nullptr节点
iterator End()
{
return iterator(nullptr);
}
直接用迭代器创建一个对象并传nullptr
3. operator*和operator->的实现
和链表一样,operator*返回节点中的数据operator->返回节点数据的地址(原因已在链表迭代器实现已解释,不做过多赘述)
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
4. operator++和operator--的实现
由于begin(),是按照中序的方式访问的,++可以分为两种情况,当右子树不存在为空时,找孩子是祖先的左的那个祖先节点,当右子树存在不为空时,找右子树的最左节点
有如下图红黑树:
一开始begin()迭代器应该位于节点为1处,当我们要++遍历这颗红黑树时:
由于1的右子树不为空,找右子树的最左节点,最左节点为空,则++以后访问到是6,由于6的右子树为空,再++应该去找孩子是祖先的左的那个祖先节点,此时找到就是节点为8的祖先节点,此时发现,这就已经按中序的方式运行起来了
++迭代器的实现方式如下代码:
Self& operator++()
{
if (_node->_right == nullptr)
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && parent->_right == cur)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
else
{
Node* subleft = _node->_right;
while (subleft->_left)
{
subleft = subleft->_left;
}
_node = subleft;
}
return *this;
}
Self operator++(int)
{
Self tmp(*this);
++(*this);
return tmp;
}
由于begin(),是按照中序的方式访问的,--可以分为两种情况,当左子树不存在为空时,找孩子是祖先的右的那个祖先节点,当左子树存在不为空时,找左子树的最右节点
以下图红黑树为例:
假设此时迭代器已经遍历到节点为11的地方:
当我们要--遍历红黑树时,由于11的左子树不存在为空,往上找孩子是祖先的右的祖先节点,我发现11就是8的右,于是迭代器访问的是节点8的祖先节点,再次--,此时节点8的左子树存在不为空,找左子树的最右节点,此时节点6是1的最右节点,因此找到的节点是节点6,此时,发现已经是在按中序的方式遍历了
--的迭代器的实现代码如下:
Self& operator--()
{
if (_node->_left == nullptr)
{
// 找祖先里面,孩子是父亲
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
else
{
// 左子树的最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
return *this;
}
Self operator--(int)
{
Self tmp(*this);
--(*this);
return tmp;
}
5. operator!=和operator==的实现
比两个对象中的节点即可
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s->_node;
}
至此迭代器的基本实现已完成
3. 对RBTree类进行改变
由于模板参数的改变和迭代器的实现,使RBTree的实现更加完整,需要对RBTree类进行改变。
1. 改造insert
首先就是对insert进行改变,有了迭代器的实现,我们应该和库里面的一样,返回的是pair<iterator, bool>而不是bool,以及在比较的时候应该使用KeyOfT这个仿函数模板参数来接受K,修改完成代码如下:
pair<iterator, bool> Insert(const T& data)
{
// 1、搜索树的规则插入
// 2、看是否违反平衡规则,如果违反就需要处理:旋转
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), true);
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
// 存在连续红色节点
while (parent && parent->_col == RED)
{
Node* grandfater = parent->_parent;
assert(grandfater);
if (grandfater->_left == parent)
{
Node* uncle = grandfater->_right;
// 情况一:
if (uncle && uncle->_col == RED) // 叔叔存在且为红
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}
else // 叔叔不存在 或者 叔叔存在且为黑
{
if (cur == parent->_left) // 单旋
{
// g
// p
// c
RotateR(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else // 双旋
{
// g
// p
// c
RotateL(parent);
RotateR(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
else //(grandfater->_right == parent)
{
Node* uncle = grandfater->_left;
// 情况一:
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
// g
// p
// c
RotateL(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else // 双旋
{
// g
// p
// c
RotateR(parent);
RotateL(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);//创建并返回iterator对象
}
相比原来的insert对返回值做了修改,并用KeyOfT仿函数创建对象来接受其中的K,来进行比较
2. 增加find的实现
利用KeyOfT提取T中的K来进行比较找想要找的元素,找到返回迭代器元素位置的迭代器否则返回end(),具体代码实现如下:
iterator 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 iterator(cur);
}
}
return End();
}
4. 改造后的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)
:_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;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
// 休息17:00
Self& operator++()
{
if (_node->_right == nullptr)
{
// 找祖先里面,孩子是父亲左的那个
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && parent->_right == cur)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
else
{
// 右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
return *this;
}
Self operator++(int)
{
Self tmp(*this);
++(*this);
return tmp;
}
Self& operator--()
{
if (_node->_left == nullptr)
{
// 找祖先里面,孩子是父亲
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
else
{
// 左子树的最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
return *this;
}
Self operator--(int)
{
Self tmp(*this);
--(*this);
return tmp;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s->_node;
}
};
// T决定红黑树存什么数据
// set RBTree<K, K>
// map RBTree<K, pair<K, V>>
// KeyOfT -> 支持取出T对象中key的仿函数
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;
// 构造 拷贝构造 赋值 和析构 跟搜索树实现方式是一样的
iterator Begin()
{
Node* subLeft = _root;
while (subLeft && subLeft->_left)
{
subLeft = subLeft->_left;
}
return iterator(subLeft);
}
iterator End()
{
return iterator(nullptr);
}
const_iterator Begin() const
{
Node* subLeft = _root;
while (subLeft && subLeft->_left)
{
subLeft = subLeft->_left;
}
return const_iterator(subLeft);
}
const_iterator End() const
{
return const_iterator(nullptr);
}
pair<iterator, bool> Insert(const T& data)
{
// 1、搜索树的规则插入
// 2、看是否违反平衡规则,如果违反就需要处理:旋转
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), true);
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
// 存在连续红色节点
while (parent && parent->_col == RED)
{
Node* grandfater = parent->_parent;
assert(grandfater);
if (grandfater->_left == parent)
{
Node* uncle = grandfater->_right;
// 情况一:
if (uncle && uncle->_col == RED) // 叔叔存在且为红
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}
else // 叔叔不存在 或者 叔叔存在且为黑
{
if (cur == parent->_left) // 单旋
{
// g
// p
// c
RotateR(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else // 双旋
{
// g
// p
// c
RotateL(parent);
RotateR(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
else //(grandfater->_right == parent)
{
Node* uncle = grandfater->_left;
// 情况一:
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
// 继续往上处理
cur = grandfater;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
// g
// p
// c
RotateL(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else // 双旋
{
// g
// p
// c
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 (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (parent == ppNode->_left)
{
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;
}
}
iterator 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 iterator(cur);
}
}
return End();
}
private:
Node* _root = nullptr;
};
二. 封装set
set的底层结构就是红黑树,因此在set中直接封装一棵红黑树,然后将其接口包装下即可
由于在RBTree中传了KeyOfT来获取K,因此我们在set中需要实现一个仿函数来获取set的K
template<class K>
class set
{
//给红黑树获取K
struct SetkeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
其他都是包装RBTree的接口即可
注意:
由于set的Key是不能改变的,所以set的iterator应该用const_iterator包装。
但是会进一步导致insert函数在返回时,由于RBTree的iterator里的参数没有被const修饰是普通迭代器,但是set的iterator是const_iterator,就会出现iterator向const_iterator的转化,这是两个封装后毫无关系的类型,无法直接转化,因此我们需要先提取出来返回值稍作修改后重新封装,因此insert应该做出如下改造:
pair<iterator, bool> insert(const K& key)
{
//pair<typename RBTree<K, K, SetkeyOfT>::iterator, bool> ret = _t.Insert(key);
auto ret = _t.Insert(key);
return pair<iterator, bool>(iterator(ret.first._node), ret.second);
}
注意:
这里和list实现迭代器一样,当我们typedef迭代器时,会出现内嵌类型的问题,需要使用到typename关键字
set具体封装的实现代码:
#include "RBTree.h"
namespace mystl
{
template<class K>
class set
{
//给红黑树获取K
struct SetkeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetkeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetkeyOfT>::const_iterator const_iterator;
//set不能修改,只提供const版本
iterator begin() const
{
return _t.Begin();
}
iterator end() const
{
return _t.End();
}
pair<iterator, bool> insert(const K& key)
{
//pair<typename RBTree<K, K, SetkeyOfT>::iterator, bool> ret = _t.Insert(key);
auto ret = _t.Insert(key);
return pair<iterator, bool>(iterator(ret.first._node), ret.second);
}
iterator find(const K& key)
{
return _t.find(key);
}
private:
RBTree<K, K, SetkeyOfT> _t;
};
}
三. 封装map
这里需要的注意事项和set,唯一不同的是这里insert没有set的问题,因为map的iterator就是RBTree的普通迭代器封装的,因map的V是可以更改的,map和set的K是不能更改的,所以map的普通迭代器应该封装成普通的迭代器供V改变,因此,insert可以直接返回封装的insert的返回值。
map实现了operator[],这里也可以实现,直接封装RBTree的insert,返回值是RBTree返回值的V的引用供修改即可
实现代码如下:
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
其他直接封装RBTree的接口即可,j具体实现代码如下:
#include "RBTree.h"
namespace mystl
{
template<class K, class V>
class map
{
//给红黑树获取K
struct MapkeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<K, V>, MapkeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<K, V>, MapkeyOfT>::const_iterator const_iterator;
//map可以修改都提供
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
pair<iterator, bool> insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
iterator find(const K& key)
{
return _t.find(key);
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<K, V>, MapkeyOfT> _t;
};
}