所谓低层被高层封装就是低层类要作为高层类的成员变量。比如红黑树和迭代器,它们的成员变量都是一个树的节点;而map和set类的成员变量是同一颗红黑树,它们的接口都是通过直接或间接调用红黑树的接口实现的。
节点类是一个类模板,模板参数ValueType就是节点存储的数据的类型。成员变量包括指向父亲、左右孩子的指针,节点的颜色和节点存储的数据。成员函数就只有一个构造函数,传入一个ValueType的数据来构造一个节点。
// 枚举定义颜色
enum COLOR
{
RED,
BLACK
};
// 节点的定义
template<class ValueType>
struct RBTreeNode
{
// 构造函数,颜色默认给为红色
RBTreeNode(const ValueType& data)
:_color(RED)
,_data(data)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
COLOR _color;
ValueType _data;
RBTreeNode<ValueType>* _parent;
RBTreeNode<ValueType>* _left;
RBTreeNode<ValueType>* _right;
};
底层封装一个树节点的指针_node,所以构造函数是传入一个树节点的指针让_node指向它。
template<class ValueType, class Ref, class Ptr>
class RBTreeItetator
{
typedef RBTreeNode<ValueType> Node;
typedef RBTreeItetator<ValueType, Ref, Ptr> self;
public:
RBTreeItetator(Node* node)
:_node(node)
{}
private:
Node* _node;
};
为了实现const迭代器,模板参数除了节点的数据类型外还引入了数据类型的引用(Reference)和数据类型的指针(Pointer)。在迭代器重载解引用和箭头运算符时,不论外面传入的模板参数是什么,都返回Ref和Ptr,可以看到这两个模板参数的引入使得代码更加灵活,这是模板优点的体现,也是泛型编程思想的一个体现。
先看自增,我们想要这个迭代器指向下一个节点,根据中序遍历的规则可以得到下面结论:
自减操作的话,就是找前一个节点,和自增的思路相反,不再赘述。它们的实现如下:
// 前置自增操作
self& operator++()
{
if(_node->_right)
{
Node* subRL=_node->_right;
while(subRL->_left)
{
subRL=subRL->_left;
}
_node=subRL;
}
else
{
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)
{
Node* subLR=_node->_left;
while(subLR->_right)
{
subLR=subLR->_right;
}
_node=subLR;
}
else
{
Node* cur=_node;
Node* parent=cur->_parent;
while(parent && cur==parent->_left)
{
cur=parent;
parent=parent->_parent;
}
_node=parent;
}
return *this;
}
解引用就是返回节点中存储的数据的引用,箭头返回节点中存储数据的指针。
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
编译器对箭头操作符的特殊处理
所谓迭代器就是模拟像指针一样的东西,我们希望通过解引用迭代器来直接获取所存储的数据对象本身,我们上面的实现确实做到了;我们也希望通过箭头操作符操作迭代器直接访问存储数据内的成员,在使用箭头操作符时会遇到一些问题。
比较两个迭代器是否相等,就是比较底层的成员变量即节点对象是否相等。
bool operator==(const self& it)
{
return _node == it._node;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
// 迭代器
template<class ValueType, class Ref, class Ptr>
class RBTreeItetator
{
typedef RBTreeNode<ValueType> Node;
typedef RBTreeItetator<ValueType, Ref, Ptr> self;
public:
RBTreeItetator(Node* node)
:_node(node)
{}
self& operator++()
{
if(_node->_right)
{
Node* subRL=_node->_right;
while(subRL->_left)
{
subRL=subRL->_left;
}
_node=subRL;
}
else
{
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)
{
Node* subLR=_node->_left;
while(subLR->_right)
{
subLR=subLR->_right;
}
_node=subLR;
}
else
{
Node* cur=_node;
Node* parent=cur->_parent;
while(parent && cur==parent->_left)
{
cur=parent;
parent=parent->_parent;
}
_node=parent;
}
return *this;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator==(const self& it)
{
return _node == it._node;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
private:
Node* _node;
};
成员变量有两个:记录该树节点个数的_size和该树的根_root。
// 树的定义
template<class Key, class ValueType, class KeyOfValue>
class RBTree
{
typedef RBTreeNode<ValueType> Node;
public:
typedef RBTreeItetator<ValueType, ValueType&, ValueType*> Iterator;
typedef RBTreeItetator<ValueType, const ValueType&, const ValueType*> const_Iterator;
RBTree()
:_size(0)
,_root(nullptr)
{}
private:
size_t _size;
Node* _root;
};
关于typedef的几点说明
我们对节点类重定义为Node,后面会经常用到为了方便写才重定义的,权限为private因为我们只在类内部使用。另外我们还对迭代器进行了重定义,包括const迭代器和非const迭代器,为了可读性和写起来便捷才重定义,权限为public因为有从外部读取的需要:
如何用一棵红黑树同时实现map和set?
通过对模板参数的巧妙设计可以实现。第一个模板参数Key是节点排序和比较的唯一标识,如果是set的话key就是它自己本身存储的数据,如果是map的话key就是键值对的第一个值。第二个模板参数是map和set存储的数据,map的话是pair键值对,set的话是key。
关于Insert中插入数据的比较方式
此时红黑树的第三个参数就起作用了,它是一个仿函数,map、set各自实现一个并传递给一同颗红黑树。KeyOfValue,顾名思义就是获取ValueType中的Key,即存储数据作为比较的唯一表示。虽然map和set的对KeyOfValue实现不同,但可以完美地匹配它们自己的ValuType,当插入时统一调用这个仿函数来获取Key进行比较,这又是一种泛型编程的体现。
第一个模板参数的作用?
第二个模板参数和第三个模板参数我们都在Insert()中实用到了,传入的第一个模板参数Key有什么用呢?在Find()中就会用到第一个模板参数,我们调用Find()时传入的实参类型就是Key。
红黑树的成员变量是整棵树的根节点,begin()要返回第一个节点的迭代器,根据中序遍历的规则往前找就是去找左子树的最左节点;end()的话就返回一个空的迭代器。最后普通对象返回普通迭代器,const对象返回const迭代器,至于对象是普通的还是const的由在我们在创建map或set对象时它们的属性来决定,因为map和set底层封装红黑树,它们的对象如果是const的话,封装的红黑树也是const的,对应返回const迭代器。
Iterator Begin()
{
Node* subL=_root;
while(subL && subL->_left)
{
subL=subL->_left;
}
return Iterator(subL);
}
const_Iterator Begin() const
{
Node* subL=_root;
while(subL && subL->_left)
{
subL=subL->_left;
}
return const_Iterator(subL);
}
Iterator End()
{
return Iterator(nullptr);
}
const_Iterator End() const
{
return const_Iterator(nullptr);
}
红黑树除了根节点外还有一个记录当前节点数的成员变量_size。
empty()的话就是判断_size是否等于0。
size()的话直接返回_size的值就行。
size_t Size() const
{
return _size;
}
bool Empty() const
{
return _size == 0;
}
这些都是红黑树的基本操作,没有什么太多特别的和与其他类相关联的东西,相关代码放到下面的完整代码里。一定要说的话就是他们的返回值:
// 红黑树的定义
template<class Key, class ValueType, class KeyOfValue>
class RBTree
{
typedef RBTreeNode<ValueType> Node;
public:
typedef RBTreeItetator<ValueType, ValueType&, ValueType*> Iterator;
typedef RBTreeItetator<ValueType, const ValueType&, const ValueType*> const_Iterator;
RBTree()
:_size(0)
,_root(nullptr)
{}
Iterator Begin()
{
Node* subL=_root;
while(subL && subL->_left)
{
subL=subL->_left;
}
return Iterator(subL);
}
const_Iterator Begin() const
{
Node* subL=_root;
while(subL && subL->_left)
{
subL=subL->_left;
}
return const_Iterator(subL);
}
Iterator End()
{
return Iterator(nullptr);
}
const_Iterator End() const
{
return const_Iterator(nullptr);
}
size_t Size() const
{
return _size;
}
bool Empty() const
{
return _size == 0;
}
// 插入一个节点
pair<Iterator, bool> Insert(const ValueType& data)
{
// 第一步:按搜索树规则插入一个节点
// 1、空树的话插入节点作为根节点
// 2、不空的话,按搜索树规则找到插入的位置,然后插入
if(!_root)
{
_root=new Node(data);
_root->_color=BLACK;
++_size;
return make_pair(Iterator(_root), true);
}
// 寻找插入的位置
KeyOfValue kov;
Node* parent=nullptr;
Node* cur=_root;
while(cur)
{
parent=cur;
if(kov(cur->_data) == kov(data))
{
return make_pair(Iterator(cur), false);
}
else if(kov(cur->_data) < kov(data))
{
cur=cur->_right;
}
else if(kov(cur->_data) > kov(data))
{
cur=cur->_left;
}
}
// 找到后插入
Node* newNode=new Node(data);
newNode->_parent=parent;
if(kov(parent->_data) > kov(data))
{
parent->_left=newNode;
}
else if(kov(parent->_data) < kov(data))
{
parent->_right=newNode;
}
// 第二步:调整节点的颜色
// 1、如果父亲颜色为黑就不用调整
// 2、如果父亲节点颜色为红,调整方式由叔叔决定
// a、叔叔存在且为红 --> 把叔叔和父亲的颜色调整为黑,爷爷颜色调整为红,然后继续往上调整
// b、叔叔不存在或存在且为黑 --> 看爷爷、父亲、和cur的相对位置来决定用什么旋转,旋转后调整父亲和爷爷的颜色
cur=newNode;
while(parent && parent->_color == RED)
{
// 既然进来了这个循环内部,说明父亲一定是红色,那么一定有爷爷且爷爷颜色为黑
Node* grandfather = parent->_parent;
// 确定叔叔的位置
if(grandfather->_left == parent)
{
Node* uncle=grandfather->_right;
if(uncle && uncle->_color == RED)// 叔叔存在且为红
{
parent->_color=uncle->_color=BLACK;
grandfather->_color=RED;
cur=grandfather;
parent=cur->_parent;
}
else // 叔叔不存在或者存在且为黑
{
if(cur == parent->_right)
{
RotateL(parent);
std::swap(parent,cur);
}
RotateR(grandfather);
parent->_color=BLACK;
grandfather->_color=RED;
break;
}
}
else if(grandfather->_right == parent)
{
Node* uncle=grandfather->_left;
if(uncle && uncle->_color==RED)// 叔叔存在且为红
{
uncle->_color=parent->_color=BLACK;
grandfather->_color=RED;
cur=grandfather;
parent=cur->_parent;
}
else // 叔叔不存在或存在且为黑
{
if(cur == parent->_left)
{
RotateR(parent);
std::swap(parent,cur);
}
RotateL(grandfather);
parent->_color=BLACK;
grandfather->_color=RED;
break;
}
}
}
_root->_color=BLACK;
++_size;
return make_pair(Iterator(newNode), true);
}
KeyOfValue kov;
Iterator Find(const Key& k)
{
Node* cur=_root;
while(cur)
{
if(kov(cur->_data) > k)
{
cur=cur->_left;
}
else if(kov(cur->_data) < k)
{
cur=cur->_right;
}
else
{
return Iterator(cur);
}
}
return Iterator(nullptr);
}
const_Iterator Find(const Key& k) const
{
Node* cur=_root;
while(cur)
{
if(kov(cur->_data) > k)
{
cur=cur->_left;
}
else if(kov(cur->_data) < k)
{
cur=cur->_right;
}
else
{
return const_Iterator(cur);
}
}
return const_Iterator(nullptr);
}
private:
// 左单旋
void RotateL(Node* parent)
{
Node* subR=parent->_right;
Node* subRL=subR->_left;
parent->_right=subRL;
if(subRL != nullptr)
{
subRL->_parent=parent;
}
subR->_left=parent;
Node* pparent=parent->_parent;
parent->_parent=subR;
if(pparent == nullptr)
{
_root=subR;
subR->_parent=nullptr;
}
else
{
subR->_parent=pparent;
if(pparent->_left == parent)
{
pparent->_left=subR;
}
else if(pparent->_right == parent)
{
pparent->_right=subR;
}
}
}
// 右单旋
void RotateR(Node* parent)
{
Node* subL=parent->_left;
Node* subLR=subL->_right;
parent->_left=subLR;
if(subLR != nullptr)
{
subLR->_parent=parent;
}
subL->_right=parent;
Node* pparent=parent->_parent;
parent->_parent=subL;
if(pparent == nullptr)
{
_root=subL;
subL->_parent=nullptr;
}
else
{
subL->_parent=pparent;
if(pparent->_left == parent)
{
pparent->_left=subL;
}
else if(pparent->_right == parent)
{
pparent->_right=subL;
}
}
}
size_t _size;
Node* _root;
};
它们两个的成员变量都是一颗红黑树,并且都自己实现仿函数KeyOfValue。不同点在于模板参数不同,map因为存储键值对所以有两个模板参数;set只有一个模板参数Key。
// map的基本框架
template<class K, class V>
class MyMap
{
typedef pair<K, V> ValueType;
struct KeyOfValue
{
const K& operator()(const ValueType& v)
{
return v.first;
}
}
private:
RBTree<K, ValueType, KeyOfValue> _t;
};
// set的基本框架
template<class K>
class MySet
{
typedef K ValueType;
struct KeyOfValue
{
const K& operator()(const ValueType& k)
{
return k;
}
};
private:
RBTree<K, ValueType, KeyOfValue> _t;
};
map、set都有的接口包括begin()、end()、empty()、size()、Find、Insert等等,看到上面这些接口有没有觉得很熟悉?没错,这些都是上面红黑树已经实现了的接口,map和set因为底层是红黑树所以可以直接调用这些接口,至于有多直接,从它们两个的完整代码里可见一斑。
map和set相当于红黑树的的一件衣服,对内把红黑树装饰和保护了起来,对外使得使用者操作红黑树更加方便、容易理解。
map
这里要注意一下 [ ] 这个操作符的重载,这是map独有的,底层通过封装Insert实现。方括号内我们传入Key,如果存在的话返回Value的引用;如果不存在,则新插入这个节点,它的Value为对应类型的一个匿名对象,调用其默认构造函数。即该操作符的功能是无论如何都返回给你对应key的value。
template<class K, class V>
class MyMap
{
typedef pair<K, V> ValueType;
struct KeyOfValue
{
const K& operator()(const ValueType& v)
{
return v.first;
}
};
public:
typedef typename RBTree<K, ValueType, KeyOfValue>::Iterator Iterator;
typedef typename RBTree<K, ValueType, KeyOfValue>::const_Iterator const_Iterator;
pair<Iterator, bool> Insert(const ValueType& v)
{
return _t.Insert(v);
}
Iterator Begin()
{
return _t.Begin();
}
const_Iterator Begin() const
{
return _t.Begin();
}
Iterator End()
{
return _t.End();
}
const_Iterator End() const
{
return _t.End();
}
size_t Size() const
{
return _t.Size();
}
bool Empty() const
{
return _t.Empty();
}
Iterator Find(const K& k)
{
return _t.Find(k);
}
const_Iterator Find(const K& k) const
{
return _t.Find(k);
}
V& operator[](const K& k)
{
pair<Iterator, bool> ret=_t.Insert(make_pair(k, V()));
return ret.first->second;
}
private:
RBTree<K, ValueType, KeyOfValue> _t;
};
set
template<class K>
class MySet
{
typedef K ValueType;
struct KeyOfValue
{
const K& operator()(const ValueType& k)
{
return k;
}
};
public:
typedef typename RBTree<K, ValueType, KeyOfValue>::Iterator Iterator;
typedef typename RBTree<K, ValueType, KeyOfValue>::const_Iterator const_Iterator;
pair<Iterator, bool> Insert(const ValueType& k)
{
return _t.Insert(k);
}
Iterator Begin()
{
return _t.Begin();
}
const_Iterator Begin() const
{
return _t.Begin();
}
Iterator End()
{
return _t.End();
}
const_Iterator End() const
{
return _t.End();
}
bool Empty() const
{
return _t.Empty();
}
size_t Size() const
{
return _t.Size();
}
Iterator Find(const K& k)
{
return _t.Find(k);
}
const_Iterator Find(const K& k) const
{
return _t.Find(k);
}
private:
RBTree<K, ValueType, KeyOfValue> _t;
};