【C++篇】红黑树的实现
目录
前言:
一,红黑树的概念
1.1,红黑树的规则
1.2,红黑树的最长路径
1.3,红黑树的效率分析
二,红黑树的实现
2.1,红黑树的结构
2.2,红黑树的插入
2.2.1,大致过程
2.2.2,情况1:变色处理
2.2.3,情况2:单旋+变色
2.2.4,情况3:双旋+变色
2.3,红黑树插入代码的实现
2.4,红黑树的验证
三,整体代码
四,测试代码
前言:
本篇会用到上篇【AVL树的实现】中的旋转知识。
一,红黑树的概念
红黑树是一颗二叉搜索树,它的每一个节点增加一个存储为来表示节点的颜色。可以是红色或者黑色。它通过对从根开始到叶子节点的每条路径上各个节点颜色的约束,确保最长路径不会超过最短路径的2倍,从而实现平衡的。
1.1,红黑树的规则
1,每个节点不是红色就是黑色。
2,根节点是黑色的。
3,红色节点的两个孩子只能是 黑色节点或者是空节点。也就是说不能出现连续的红色节点
4,对于任意一个节点,从该节点开始,到叶子节点的所有路径上,均包含相同数量的黑色节点。
以上 都是红黑树,满足红黑树的规则。
1.2,红黑树的最长路径
1,由第四条规则可知,从根节点开始的每条路径上,黑色节点的数量相同。所以在极端场景下,一颗红黑树,它的最短路径就是节点全为黑色的路径。假设红黑树的每条路径黑色节点数量都为b,那么最短路径的节点数量为b.
2,由 第三条规则可知,一条路径上不能由连续的红色节点,最长路径是由一黑一红间隔组成的,所以最长路径为2*b。
3,而对于一颗红黑树,最长和最短路径不一定存在。我们可以得出对于任意一颗红黑树,它的任意 一条路径长度x都是,b<=x<=2*b.
1.3,红黑树的效率分析
假设N是红黑树节点的数量,h是最短路径的长度,最长路径不超过2*h
可以得到2^h-1<= N <= 2^(2*h)-1,推出h大致为logN,也就意味着红黑树的增删查该最坏走2*logN,时间复杂度O(logN).
二,红黑树的实现
2.1,红黑树的结构
enum color
{
Red,
Black
};template<class k,class v>
struct RBTreeNode
{
RBTreeNode(const pair<k,v>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
{}
RBTreeNode<k, v>* _left;
RBTreeNode<k, v>* _right;
RBTreeNode<k, v>* _parent;
pair<k, v> _kv;
color _col;
};template<class k,class v>
class RBTree
{
typedef RBTreeNode<k, v> Node;
public://...
private:
Node* _root=nullptr;
};
2.2,红黑树的插入
2.2.1,大致过程
1,插入一个值需要按照搜索树的规则进行插入,再判断插入后是否满足红黑树的规则。
2,如果是空树插入,新增节点就是黑色节点。如果是非空树插入,新增节点就必须是红色节点,因为如果插入黑色节点,就一定会破坏规则4,而插入红色节点是有可能会破坏规则3,而且对于规则3来说,解决方法比规则4的解决方法容易。
3,非空树插入后,如果父节点是黑色节点,则没有违反任何规则,插入结束。
4,非空树插入后,如果父节点是红色节点,则违反规则3,进一步分析。
2.2.2,情况1:变色处理
由上图可知,c为红,p为红,g为黑,u存在且为红。在这种情况下,我们需要将p和u变黑,g变红,g成为新的c,继续向上更新。
分析:
因为p和u都是红色的,g是黑色的。把p和u变黑,左边子树路径各增加一个黑色节点,g再变红,相当于g所在路径的黑色节点数量不变,同时解决了c和p连续红节点的问题。
需要继续往上跟新是因为g是红色,如果g的父亲还是红色,就需要继续处理;如果g的父亲是黑色,则处理结束;如果g是整棵树的根,再将g变成黑色即可。
2.2.3,情况2:单旋+变色
c为红,p为红,u不存在或者u存在且u为黑色。在这种情况下,就需要进行单旋+变色处理
分析:
u不存在时,c一定是新增节点。
u存在且为黑色时,c一定不是新增节点,是在c的子树中插入,符号情况1,经过情况1后,c被调整为红色的。
上图展示的是右单旋的场景,下面是根据右单旋,画出的左单旋大致图,与右单旋过程相似:
总结:经过单旋+变色后,我们可以看到p做了子树新的根,且p是黑色的,所以不管p的父亲是黑色的还是红色的,都满足红黑树的规则,所以此时,就不需要往上更新了。
2.2.4,情况3:双旋+变色
c为红,p为红,g为黑,u不存在或者u存在且为黑,u不存在,则c一定是新增结点,u存在且为黑,则 c一定不是新增,c之前是黑色的,是在c的子树中插入,符合情况1,变色将c从黑色变成红色,更新上来的。
上图展示的是左右双旋+变色,同样右左双旋类似:
同样经过双旋+变色后,c成为新的根,且c为黑色,所以也不需要继续向上更新了。
2.3,红黑树插入代码的实现
bool Insert(const pair<k, v>& kv)
{
//插入根节点,color->Black
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
//根据搜索树的规则查找插入位置
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur =cur->_left;
}
else
{
return false;
}
}
//插入,新节点的颜色为红色
cur = new Node(kv);
cur->_col = Red;
//与父亲节点连接好
if (parent->_kv.first < kv.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
//颜色处理+旋转
while (parent&& parent->_col == Red)
{
//g
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
//p为g的左边
// g
// p u
Node* uncle = grandfather->_right;
//叔叔存在且为红,情况1
if (uncle && uncle->_col == Red)
{
//变色
parent->_col = Black;
uncle->_col = Black;
grandfather->_col = Red;
//继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
//情况2,3
//叔叔不存在或者叔叔为黑
//u为黑,则c之前是黑的
//u不存在,则c是新插入的
if (cur == parent->_left)
{
//右单旋场景
// g
// p u
// c
RotateR(grandfather);
//变色
parent->_col = Black;
grandfather->_col = Red;
}
else
{
//左右双旋场景
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
//变色
cur->_col = Black;
grandfather->_col = Red;
}
//不需要进行向上更新
break;
}
}
else
{
//p为g的右边
// g
// u p
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == Red)
{
//情况1
//变色
parent->_col = Black;
uncle->_col = Black;
grandfather->_col = Red;
cur = grandfather;
parent = cur->_parent;
}
else
{
//情况2,3
if (cur == parent->_right)
{
//左单旋场景
// g
// u p
// c
RotateL(grandfather);
//变色
parent->_col = Black;
grandfather->_col = Red;
}
else
{
//右左双旋场景
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
//变色
cur->_col = Black;
grandfather->_col = Red;
}
//不需要继续向上更新
break;
}
}
}
//跟的颜色可能被调整为红色,最后一步改为黑色即可
_root->_col = Black;
return true;
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* pparent = parent->_parent;
if (subLR)
subLR->_parent = parent;
parent->_left = subLR;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
}
//左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* pparent = parent->_parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
parent->_parent = subR;
subR->_left = parent;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
}
2.4,红黑树的验证
我们只需验证形成的树是否满足红黑树的四条规则即可。
其中,规则1和规则 2可以直接检查。
对于规则3,我们可以进行前序遍历,遍历到一个节点,判断该节点的颜色,再判断它的两个孩子的颜色,这样做太麻烦了。我们可以反过来,遍历到一个节点,如果他是红色的,判断它的父亲节点是否为黑色。
对于规则4,我们可以先从根开始找到一条路径上黑色节点的个数refNum,再对整棵树进行前序遍历,用变量 blackNum记录黑色节点的个数,当遍历到空的时候,与refNum比较即可。
bool Check(Node* root, int BlackNum, int num)
{
if (root == nullptr)
{
if (BlackNum != num)
return false;
return true;
}
if (root->_col == Red && root->_parent->_col == Red)
return false;
if (root->_col == Black)
BlackNum++;
return Check(root->_left, BlackNum, num) && Check(root->_right, BlackNum, num);
}
//验证
bool isbalance()
{
if (_root == nullptr)
return true;
if (_root->_col == Red)
return false;
int num = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == Black)
num++;
cur = cur->_left;
}
return Check(_root, 0, num);
}
三,整体代码
enum color
{
Red,
Black
};
template<class k,class v>
struct RBTreeNode
{
RBTreeNode(const pair<k,v>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
{}
RBTreeNode<k, v>* _left;
RBTreeNode<k, v>* _right;
RBTreeNode<k, v>* _parent;
pair<k, v> _kv;
color _col;
};
template<class k,class v>
class RBTree
{
typedef RBTreeNode<k, v> Node;
public:
bool Insert(const pair<k, v>& kv)
{
//插入根节点,color->Black
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
//根据搜索树的规则查找插入位置
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur =cur->_left;
}
else
{
return false;
}
}
//插入,新节点的颜色为红色
cur = new Node(kv);
cur->_col = Red;
//与父亲节点连接好
if (parent->_kv.first < kv.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
//颜色处理+旋转
while (parent&& parent->_col == Red)
{
//g
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
//p为g的左边
// g
// p u
Node* uncle = grandfather->_right;
//叔叔存在且为红,情况1
if (uncle && uncle->_col == Red)
{
//变色
parent->_col = Black;
uncle->_col = Black;
grandfather->_col = Red;
//继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
//情况2,3
//叔叔不存在或者叔叔为黑
//u为黑,则c之前是黑的
//u不存在,则c是新插入的
if (cur == parent->_left)
{
//右单旋场景
// g
// p u
// c
RotateR(grandfather);
//变色
parent->_col = Black;
grandfather->_col = Red;
}
else
{
//左右双旋场景
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
//变色
cur->_col = Black;
grandfather->_col = Red;
}
//不需要进行向上更新
break;
}
}
else
{
//p为g的右边
// g
// u p
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == Red)
{
//情况1
//变色
parent->_col = Black;
uncle->_col = Black;
grandfather->_col = Red;
cur = grandfather;
parent = cur->_parent;
}
else
{
//情况2,3
if (cur == parent->_right)
{
//左单旋场景
// g
// u p
// c
RotateL(grandfather);
//变色
parent->_col = Black;
grandfather->_col = Red;
}
else
{
//右左双旋场景
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
//变色
cur->_col = Black;
grandfather->_col = Red;
}
//不需要继续向上更新
break;
}
}
}
//跟的颜色可能被调整为红色,最后一步改为黑色即可
_root->_col = Black;
return true;
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* pparent = parent->_parent;
if (subLR)
subLR->_parent = parent;
parent->_left = subLR;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
}
//左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* pparent = parent->_parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
parent->_parent = subR;
subR->_left = parent;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
}
void Inorder()
{
_Inorder(_root);
}
int Height()
{
return _Height(_root);
}
int size()
{
return _size(_root);
}
Node* Find(const k& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
//验证
bool isbalance()
{
if (_root == nullptr)
return true;
if (_root->_col == Red)
return false;
int num = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == Black)
num++;
cur = cur->_left;
}
return Check(_root, 0, num);
}
private:
bool Check(Node* root, int BlackNum, int num)
{
if (root == nullptr)
{
if (BlackNum != num)
return false;
return true;
}
if (root->_col == Red && root->_parent->_col == Red)
return false;
if (root->_col == Black)
BlackNum++;
return Check(root->_left, BlackNum, num) && Check(root->_right, BlackNum, num);
}
int _size(Node* root)
{
if (root == nullptr)
return 0;
return _size(root->_left) + _size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Inorder(root->_right);
}
Node* _root=nullptr;
};
四,测试代码
void TestRBTree1()
{
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, e });
}
t.Inorder();
cout << t.isbalance() << endl;
}
int main()
{
TestRBTree1();
return 0;
}