模拟实现
红黑树
map和set底层都是使用的红黑树,但是上层的接口有一些不同。像map的话是靠key获得value,而set因为key和value是相同的,所以涉及到value的操作全都等同于key,比如find。所以需要对红黑树加一层封装。红黑树用的是上篇的代码:
namespace mystd {
enum Color
{
BLACK,
RED
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
pair<K, V> _kv;
Color _col;
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _col(RED)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root->_kv = kv;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_col = RED;
// 处理颜色
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else if (parent->_kv.first > kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
// 叔叔节点存在
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// LL or RR的上溢情况
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 递归上溢
cur = grandfather;
parent = cur->_parent;
}
else//叔父节点不存在
{
if (cur = parent->_left)//LL,右旋,参考AVL树
{
RorateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else//LR, cur变成grandfather,father变成left,grandfather变成right
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;//234树只有2个节点,不可能存在上滤情况,break;
}
}
else // parent = grandfather.right
{
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)
{
RotateL(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
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 (ppNode == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
ppNode->_left = subL;
else
ppNode->_right = subL;
ppNode->_parent = subL;
}
subL->_bf = 0;
parent->_bf = 0;
}
void rotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* ppNode = parent->_parent;
parent->_parent = subR;
subR->_left = parent;
if (ppNode == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
parent->_bf = 0;
subR->_bf = 0;
}
private:
Node* _root = nullptr;
};
}
set
set的迭代器调用的是RBTree的迭代器,RBTree的迭代器设计思路主要是一个基础node,在基础node上封装一层普通node,可以用继承来实现。然后定义基础的iterator_node,将普通红黑树node传进来,并实现 increment()和decrement()两个基础的,内部逻辑是 ++ 和 – ;
increment:
- 检测右孩子是否存在
- 存在,则获得右子树的最左下节点
- 不存在,获得父节点的右子树
- 若父节点右子树是自身,重复上一步
- 若不是自身,获得父节点
有一点抽象,大家可以去看set map源码阅读里红黑树那一小节的图;decrement同理。
将底层的迭代器node封装成正常的迭代器node,再实现begin():一路左下递归,end():一路右下递归等迭代器访问内容。下面set假设rbtree迭代器存在。
#include "RBTree.h"
namespace mystd
{
template<class K>
class set
{
public:
typedef typename RBTree<K, K>::iterator iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
bool insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree<K, K> _t;
};
}
map
#include "RBTree.h"
namespace mystd
{
template<class K, class V>
class map
{
public:
typedef typename RBTree<K, V>::iterator iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
bool insert(const pair<const K, V>& kv)
{
return _t.Insert(kv);
}
private:
RBTree<K, pair<const K, V>> _t;
};
}
结束语
set map主要都是调用红黑树的迭代器,所以最好还是看看源码中红黑树的迭代器的设计思想