AVL树和红黑树(map和set的底层实现)
map和set的底层结构
map和set其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。
AVL树
AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O(logN),搜索时间复杂度O(logN )。
AVL树节点的定义
template<class K, class V>
struct AVLTreeNode
{
pair<K, V> _val;
AVLTreeNode<K, V>* _pLeft;
AVLTreeNode<K, V>* _pRight;
AVLTreeNode<K, V>* _pParent;
int _bf;
AVLTreeNode(const pair<K,V>& val)
:_val(val)
, _pLeft(nullptr)//该节点的左孩子
, _pRight(nullptr)//该节点的右孩子
, _pParent(nullptr)//该节点的父节点
, _bf(0)//该节点的平衡因子
{}
};
AVL树的插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:
- 按照二叉搜索树的方式插入新节点
- 调整节点的平衡因子
bool Insert(const pair<K, V> kv)
{
// 1. 先按照二叉搜索树的规则将节点插入到AVL树中
// ...
// 2. 新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否破坏了AVL树的平衡性
/*
pCur插入后,pParent的平衡因子一定需要调整,在插入之前,pParent的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:
1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可
2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可
此时:pParent的平衡因子可能有三种情况:0,正负1, 正负2
1. 如果pParent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插入后被调整成0,此时满足AVL树的性质,插入成功
2. 如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更新成正负1,此时以pParent为根的树的高度增加,需要继续向上更新
3. 如果pParent的平衡因子为正负2,则pParent的平衡因子违反平衡树的性质,需要对其进行旋转处理
*/
if (_root == nullptr)
{
_root = new Node(kv);
_root->_bf = 0;
return true;
}
Node* Parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_val.first > kv.first)
{
Parent = cur;
cur = cur->_pLeft;
}
else if (cur->_val.first < kv.first)
{
Parent = cur;
cur = cur->_pRight;
}
else
{
return false;
}
}
cur = new Node(kv);
if (Parent->_val > cur->_val)
{
Parent->_pLeft = cur;
cur->_pParent = Parent;
}
else
{
Parent->_pRight = cur;
cur->_pParent = Parent;
}
while (Parent)
{
if (cur == Parent->_pLeft)
Parent->_bf--;
else
{
Parent->_bf++;
}
if (Parent->_bf == 0)
break;
else if (abs(Parent->_bf) == 1)
{
cur = Parent;
Parent = Parent->_pParent;
}
else if (abs(Parent->_bf) == 2)
{
if (Parent->_bf == 2){
if (cur->_bf == 1){
RotateL(Parent);
}
else if (cur->_bf == -1){
RotateRL(Parent);
}
}
else if (Parent->_bf == -2){
if (cur->_bf == -1){
RotateR(Parent);
}
else if (cur->_bf == 1){
RotateLR(Parent);
}
}
break;
}
else
{
assert(false);
}
}
return true;
}
AVL树的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:
1. 新节点插入较高左子树的左侧—左左:右单旋
/*
上图在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左子树增
加
了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加
一层,
即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子
树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子
即可。在旋转过程中,有以下几种情况需要考虑:
1. 30节点的右孩子可能存在,也可能不存在
2. 60可能是根节点,也可能是子树
如果是根节点,旋转完成后,要更新根节点
如果是子树,可能是某个节点的左子树,也可能是右子树
同学们再此处可举一些详细的例子进行画图,考虑各种情况,加深旋转的理解
*/
void _RotateR(PNode pParent)
{
// pSubL: pParent的左孩子
// pSubLR: pParent左孩子的右孩子,注意:该
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
// 旋转完成之后,30的右孩子作为双亲的左孩子
pParent->_pLeft = pSubLR;
// 如果30的左孩子的右孩子存在,更新亲双亲
if(pSubLR)
pSubLR->_pParent = pParent;
// 60 作为 30的右孩子
pSubL->_pRight = pParent;
// 因为60可能是棵子树,因此在更新其双亲前必须先保存60的双亲
PNode pPParent = pParent->_pParent;
// 更新60的双亲
pParent->_pParent = pSubL;
// 更新30的双亲
pSubL->_pParent = pPParent;
// 如果60是根节点,根新指向根节点的指针
if(NULL == pPParent)
{
_pRoot = pSubL;
pSubL->_pParent = NULL;
}
else
{
// 如果60是子树,可能是其双亲的左子树,也可能是右子树
if(pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
// 根据调整后的结构更新部分节点的平衡因子
pParent->_bf = pSubL->_bf = 0;
}
2. 新节点插入较高右子树的右侧—右右:左单旋
//左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeft = parent;
Node* ppNode = parent->_pParent;
parent->_pParent = subR;
if (parent == _root)
{
_root = subR;
_root->_pParent = nullptr;
}
else
{
if (ppNode->_pLeft == subR)
ppNode->_pLeft = subR;
else
{
ppNode->_pRight = subR;
}
subR->_pParent = ppNode;
}
parent->_bf = subR->_bf = 0;
}
3. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。
// 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整
void _RotateLR(PNode pParent)
{
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平
衡因子
int bf = pSubLR->_bf;
// 先对30进行左单旋
_RotateL(pParent->_pLeft);
// 再对90进行右单旋
_RotateR(pParent);
if(1 == bf)
pSubL->_bf = -1;
else if(-1 == bf)
pParent->_bf = 1;
}
4. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋
void RotateRL(Node* parent)
{
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
int bf = subRL->_bf; //保存subRL的平衡因子,因为在下面两行会置 0
RotateR(parent->_pRight);
RotateL(parent);
if (bf == 0){
parent->_bf = subRL->_bf = subR->_bf = 0;
}
else if (bf == 1){ //在c插入的新结点
subR->_bf = 0;
parent->_bf = -1;
subRL->_bf = 0;
}
else if (bf == -1){ //在b插入的新结点
parent->_bf = 0;
subR->_bf = 1;
subRL->_bf = 0;
}
}
总结: 假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑
- pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
- 当pSubR的平衡因子为1时,执行左单旋
- 当pSubR的平衡因子为-1时,执行右左双旋
- pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
- 当pSubL的平衡因子为-1是,执行右单旋
- 当pSubL的平衡因子为1时,执行左右双旋
旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。
AVL树的验证
AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
- 验证其为二叉搜索树 如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
- 验证其为平衡树 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)节点的平衡因子是否计算正确
int _Height(PNode pRoot);
bool _IsBalanceTree(PNode pRoot)
{
// 空树也是AVL树
if (nullptr == pRoot) return true;
// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差
int leftHeight = _Height(pRoot->_pLeft);
int rightHeight = _Height(pRoot->_pRight);
int diff = rightHeight - leftHeight;
// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
// pRoot平衡因子的绝对值超过1,则一定不是AVL树
if (diff != pRoot->_bf || (diff > 1 || diff < -1))
return false;
// pRoot的左和右如果都是AVL树,则该树一定是AVL树
return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);
}
AVL树的删除
因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。
AVL树的性能
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:
- 插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
红黑树
红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树的性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
红黑树节点的定义
// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(color)
{}
RBTreeNode<ValueType>* _pLeft; // 节点的左孩子
RBTreeNode<ValueType>* _pRight; // 节点的右孩子
RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)
ValueType _data; // 节点的值域
Color _color; // 节点的颜色
};
红黑树结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:
红黑树的插入操作
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
1. 按照二叉搜索的树规则插入新节点
template<class ValueType>
class RBTree{
typedef RBTreeNode Node;
public:
bool Insert(const ValueType& data)
{
……
PNode& pRoot = GetRoot();
if (nullptr == pRoot)
{
pRoot = new Node(data, BLACK);
// 根的双亲为头节点
pRoot->_pParent = _pHead;
_pHead->_pParent = pRoot;
}
else
{
// 1. 按照二叉搜索的树方式插入新节点
// 2. 检测新节点插入后,红黑树的性质是否造到破坏,
// 若满足直接退出,否则对红黑树进行旋转着色处理
}
// 根节点的颜色可能被修改,将其改回黑色
pRoot->_color = BLACK;
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
return true;
}
private:
PNode& GetRoot(){ return _pHead->_pParent; }
// 获取红黑树中最小节点,即最左侧节点
PNode LeftMost();
// 获取红黑树中最大节点,即最右侧节点
PNode RightMost();
private:
Node* _root;
};
2. 检测新节点插入后,红黑树的性质是否造到破坏
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
- 约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
- 情况一: cur为红,p为红,g为黑,u存在且为红
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
- 情况二: cur为红,p为红,g为黑,u不存在/u为黑
p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反, p为g的右孩子,cur为p的右孩子,则进行左单旋转 p、g变色–p变黑,g变红
- 情况三: cur为红,p为红,g为黑,u不存在/u为黑
p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反, p为g的右孩子,cur为p的左孩子,则针对p做右单旋转 则转换成了情况2
pair<iterator, bool> Insert(const T& val){
//插入节点
if (_root == nullptr)
{
_root = new Node(val);
_root->_color = BLACK;
return make_pair(iterator(_root), true);
}
//寻找位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data < val){
parent = cur;
cur = cur->_pRight;
}
else if (cur->_data > val){
parent = cur;
cur = cur->_pLeft;
}
else{
return make_pair(iterator(cur), false);
}
}
cur = new Node(val);
Node* newnode = cur;
cur->_color = RED;
//插入适当位置
if (parent->_data < val){
parent->_pRight = cur;
cur->_pParent = parent;
}
else{
parent->_pLeft = cur;
cur->_pParent = parent;
}
//变色处理
//父节点为红色节点
while (parent && parent->_color == RED){
Node* grandfather = parent->_pParent;
//父亲是祖父节点左孩子
if (parent == grandfather->_pLeft){
Node* uncle = grandfather->_pRight;
// 1.叔叔节点存在且为红
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
//叔叔节点不存在或者叔叔节点为黑
else{
if (cur == parent->_pRight){
RotateL(parent);
swap(parent, cur);
}
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
//父亲节点是祖父节点右孩子
else{
Node* uncle = grandfather->_pLeft;
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
else{
if (cur == parent->_pLeft){
RotateR(parent);
swap(parent, cur);
}
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
}
_root->_color = BLACK;
return make_pair(iterator(newnode), true);
}
void RotateL(Node* parent){
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeft = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subR;
if (parent == _root){
_root = subR;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent)
pNode->_pLeft = subR;
else
pNode->_pRight = subR;
subR->_pParent = pNode;
}
}
void RotateR(Node* parent){
Node* subL = parent->_pLeft;
Node* subLR = subL->_pRight;
parent->_pLeft = subLR;
if (subLR)
subLR->_pParent = parent;
subL->_pRight = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subL;
if (pNode == nullptr){
_root = subL;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent){
pNode->_pLeft = subL;
}
else{
pNode->_pRight = subL;
}
subL->_pParent = pNode;
}
}
红黑树的验证
红黑树的检测分为两步:
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质
bool IsValidRBTree(){
Node* pRoot = _root;
// 空树也是红黑树
if (nullptr == pRoot)
return true;
// 检测根节点是否满足情况
if (BLACK != pRoot->_color){
cout << "违反红黑树性质二:根节点必须为黑色" << endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackCount = 0;
Node* pCur = pRoot;
while (pCur){
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){
//走到null之后,判断k和black是否相等
if (nullptr == pRoot){
if (k != blackCount){
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
return true;
}
// 统计黑色节点的个数
if (BLACK == pRoot->_color)
k++;
// 检测当前节点与其双亲是否都为红色
Node* pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color){
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
_IsValidRBTree(pRoot->_pRight, k, blackCount);
}
红黑树与AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
红黑树的迭代器
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:
- begin()与end() STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点 (最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:
// 找迭代器的下一个节点,下一个节点肯定比其大
void Increasement()
{
//分两种情况讨论:_pNode的右子树存在和不存在
// 右子树存在
if(_pNode->_pRight)
{
// 右子树中最小的节点,即右子树中最左侧节点
_pNode = _pNode->_pRight;
while(_pNode->_pLeft)
_pNode = _pNode->_pLeft;
}
else
{
// 右子树不存在,向上查找,直到_pNode != pParent->right
PNode pParent = _pNode->_pParent;
while(pParent->_pRight == _pNode)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
// 特殊情况:根节点没有右子树
if(_pNode->_pRight != pParent)
_pNode = pParent;
}
}
// 获取迭代器指向节点的前一个节点
void Decreasement()
{
//分三种情况讨论:_pNode 在head的位置,_pNode 左子树存在,_pNode 左子树不存在
// 1. _pNode 在head的位置,--应该将_pNode放在红黑树中最大节点的位置
if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
_pNode = _pNode->_pRight;
else if(_pNode->_pLeft)
{
// 2. _pNode的左子树存在,在左子树中找最大的节点,即左子树中最右侧节点
_pNode = _pNode->_pLeft;
while(_pNode->_pRight)
_pNode = _pNode->_pRight;
}
else
{
// _pNode的左子树不存在,只能向上找
PNode pParent = _pNode->_pParent;
while(_pNode == pParent->_pLeft)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
_pNode = pParent;
}
}
RBTree的完整代码
#include<iostream>
using namespace std;
enum Color{
RED,
BLACK
};
template<class ValueType>
struct RBTreeNode
{
RBTreeNode<ValueType>* _pLeft;
RBTreeNode<ValueType>* _pRight;
RBTreeNode<ValueType>* _pParent;
ValueType _data;
Color _color;
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
:_pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _color(color)
{}
};
template<class T,class Ptr,class Ref>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ptr, Ref> Self;
Node* _node;
RBTreeIterator(Node* node)
:_node(node)
{}
Ref operator*(){
return _node->_data;
}
Ref operator->(){
return &_node->_data;
}
Self& operator++()
{
if (_node->_pRight != nullptr)
{
_node = _node->_pRight;
while (_node->_pLeft != nullptr)
{
_node = _node->_pLeft;
}
}
else
{
Node* parent = _node->_pParent;
while (parent != nullptr && _node == parent->_pRight){
_node = parent;
parent = _node->_parent;
}
_node = parent;
}
return *this;
}
Self operator++(int){
Self tmp(*this);
++(*this);
return tmp;
}
Self& operator--(){
if (_node->_pLeft != nullptr)
{
_node = _node->_pLeft;
while (_node->_pLeft != nullptr)
{
_node = _node->_pRight;
}
}
else
{
Node* parent = _node->_pParent;
while (parent != nullptr && _node == parent->_pLeft){
_node = parent;
parent = _node->_parent;
}
_node = parent;
}
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;
}
};
template<class K, class T>
class RBTree{
typedef RBTreeNode<T> Node;
public:
typedef RBTreeIterator<T, T*, T&> iterator;
typedef RBTreeIterator<T, const T*, const T&> const_iterator;
RBTree() = default;
// 拷贝构造 + operator=
// 析构函数
iterator begin(){
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
iterator end(){
return iterator(nullptr);
}
const_iterator begin() const{
Node* cur = _root;
while (cur && cur->_left){
cur = cur->_left;
}
return const_iterator(cur);
}
const_iterator end() const{
return const_iterator(nullptr);
}
pair<iterator, bool> Insert(const T& val){
//插入节点
if (_root == nullptr)
{
_root = new Node(val);
_root->_color = BLACK;
return make_pair(iterator(_root), true);
}
//寻找位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data < val){
parent = cur;
cur = cur->_pRight;
}
else if (cur->_data > val){
parent = cur;
cur = cur->_pLeft;
}
else{
return make_pair(iterator(cur), false);
}
}
cur = new Node(val);
Node* newnode = cur;
cur->_color = RED;
//插入适当位置
if (parent->_data < val){
parent->_pRight = cur;
cur->_pParent = parent;
}
else{
parent->_pLeft = cur;
cur->_pParent = parent;
}
//变色处理
//父节点为红色节点
while (parent && parent->_color == RED){
Node* grandfather = parent->_pParent;
//父亲是祖父节点左孩子
if (parent == grandfather->_pLeft){
Node* uncle = grandfather->_pRight;
// 1.叔叔节点存在且为红
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
//叔叔节点不存在或者叔叔节点为黑
else{
if (cur == parent->_pRight){
RotateL(parent);
swap(parent, cur);
}
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
//父亲节点是祖父节点右孩子
else{
Node* uncle = grandfather->_pLeft;
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
else{
if (cur == parent->_pLeft){
RotateR(parent);
swap(parent, cur);
}
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
}
_root->_color = BLACK;
return make_pair(iterator(newnode), true);
}
void RotateL(Node* parent){
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeft = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subR;
if (parent == _root){
_root = subR;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent)
pNode->_pLeft = subR;
else
pNode->_pRight = subR;
subR->_pParent = pNode;
}
}
void RotateR(Node* parent){
Node* subL = parent->_pLeft;
Node* subLR = subL->_pRight;
parent->_pLeft = subLR;
if (subLR)
subLR->_pParent = parent;
subL->_pRight = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subL;
if (pNode == nullptr){
_root = subL;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent){
pNode->_pLeft = subL;
}
else{
pNode->_pRight = subL;
}
subL->_pParent = pNode;
}
}
iterator Find(const K& k){
Node* cur = _root;
while (cur){
KeyOfValue kov;
if (kov(cur->_data) < k){
cur = cur->_pRight;
}
else if (kov(cur->_data) > k){
cur = cur->_pLeft;
}
else{
return iterator(cur);
}
}
return end();
}
bool IsValidRBTree(){
Node* pRoot = _root;
// 空树也是红黑树
if (nullptr == pRoot)
return true;
// 检测根节点是否满足情况
if (BLACK != pRoot->_color){
cout << "违反红黑树性质二:根节点必须为黑色" << endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackCount = 0;
Node* pCur = pRoot;
while (pCur){
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){
//走到null之后,判断k和black是否相等
if (nullptr == pRoot){
if (k != blackCount){
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
return true;
}
// 统计黑色节点的个数
if (BLACK == pRoot->_color)
k++;
// 检测当前节点与其双亲是否都为红色
Node* pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color){
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
_IsValidRBTree(pRoot->_pRight, k, blackCount);
}
void InOrder(){
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root){
if (root == nullptr)
return;
_InOrder(root->_pLeft);
cout << root->_data << " ";
_InOrder(root->_pRight);
}
private:
Node* _root = nullptr;
};
void TestRBtree(){
RBTree<int, int> t;
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, };
for (auto e : a){
t.Insert(e);
}
t.InOrder();
cout << t.IsValidRBTree() << endl;
}
map的模拟实现
#include"RBTree.h"
namespace fxl
{
template<class K, class V>
class map
{
typedef pair<K, V> ValueType;
// 作用:将value中的key提取出来
struct KeyOfValue
{
const K& operator()(const ValueType& v)
{
return v.first;
}
};
typedef RBTree<K, ValueType, KeyOfValue> RBTree;
public:
typedef typename RBTree::Iterator iterator;
public:
map(){}
iterator begin(){ return _t.Begin(); }
iterator end(){ return _t.End(); }
size_t size()const{ return _t.Size(); }
bool empty()const{ return _t.Empty(); }
V& operator[](const K& key)
{
return (*(_t.Insert(ValueType(key, V()))).first).second;
}
const V& operator[](const K& key)const;
pair<iterator, bool> insert(const ValueType& data) { return _t.Insert(data); }
void clear(){ _t.Clear(); }
iterator find(const K& key){ return _t.Find(key); }
private:
RBTree _t;
};
}
set的模拟实现
#include"RBTree.h"
namespace fxl
{
template<class K>
class set
{
typedef K ValueType;
struct KeyOfValue
{
const K& operator()(const ValueType& key)
{
return key;
}
};
// 红黑树类型重命名
typedef RBTree<K, ValueType, KeyOfValue> RBTree;
public:
typedef typename RBTree::Iterator iterator;
public:
Set(){}
iterator begin(){ return _t.Begin(); }
iterator end(){ return _t.End(); }
size_t size()const{ return _t.Size(); }
bool empty()const{ return _t.Empty(); }
pair<iterator, bool> insert(const ValueType& data)
{
return _t.Insert(data);
}
void clear(){ _t.Clear(); }
iterator find(const K& key){ return _t.Find(key); }
private:
RBTree _t;
};
}
- Free Pascal初次体验(有亮点哦)
- HDU 1312 Red and Black(DFS,板子题,详解,零基础教你代码实现DFS)
- Selenium2+python自动化48-登录方法(参数化)
- 51Nod 1003 阶乘后面0的数量(数学,思维题)
- 如何查看某个用户指定时间段的ABAP开发记录
- Selenium2+python自动化49-判断文本(text_to_be_present_in_element)
- 洛谷 P1876 开灯(思维,枚举,规律题)
- 线性回归:简单线性回归详解
- Codeforces 789A Anastasia and pebbles(数学,思维题)
- hihoCoder #1082 : 然而沼跃鱼早就看穿了一切(字符串处理)
- 51Nod 1182 完美字符串(字符串处理 贪心 Facebook Hacker Cup选拔)
- 51Nod 1080 两个数的平方和(数论,经典题)
- Selenium3+python自动化50-环境搭建(firefox)
- Selenium2+python自动化51-unittest简介
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法