红黑树的介绍
它是一种特殊的二叉查找树,红黑树的每个节点上都有存储位表示节点的颜色,可以是红或者黑。
红黑树的特性
1.每个节点或者黑色或者红色
2.根节点是黑色
3.每个叶子节点是黑色
4.如果一个节点是红色,它的子节点必须是黑色的
5.从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点
红黑树的操作
左旋
对x进行左旋,意味着“将X变为一个左节点”
右旋
平衡调节
新插入X,父节点P,G是P的父节点,S是G的另外一个子节点。
新节点x必须为叶节点,插入的节点必须为红色;
如果P是红色,必须调整红色,根据X的插入位置,有4种情况:
1:S为黑色且X为外侧插入,先对P,G左一次单旋转,在更改P,G颜色,重新满足红黑树的规则3:
2.S为黑且X为内侧插入,先对P,X做一次单旋转并更改G,X颜色,再将结果对G做一次单旋转,满足红黑树的规则
3.S为红色,且X为外侧插入,先对P和G做一次单旋转,并改变X的颜色,此时,如果GG(G的父节点)为黑色,符合规则;否则,进入情况4
或者
4.S为红色且X为外侧插入,先对P和G做一次单旋转,并改变X的颜色,如果G的父节点,仍然为红色,继续往上做,直到父子连续部位红的情况。
或者
红黑树的实现
节点的定义
struct _Node {
_Nodeptr _Left, _Parent, _Right;
_Ty _Value;
_Redbl _color;
};
节点的父节点,左右孩子节点
初始化
申请一个节点,赋值给_Nil, 并且左右子节点为0;并在构造一个节点_Head,并且设置为红色节点,父指针指向_Nil节点,左右子节点都指向自己。
_Head节点和红黑树的根节点的父节点,是相互指向。
_Nil用来表示null节点。
_Lmost,指向最左边的节点,_Rmost,指向最右边的节点,可以用来求最小和最大值。
void _Init() {
_Nodeptr _Tmp = _Buynode(0, _Black);
{
_Lockit _Lk;
if (_Nil == 0) {
_Nil = _Tmp;
_Tmp = 0;
_Left(_Nil) = 0, _Right(_Nil) = 0;
}
++_Nilerefs;
}
if (_Tmp != 0) {
_Freenode(_Tmp);
}
_Head = _Buy(_Nil, _Red), _Size = 0;
_Lmost() = _Head, _Rmost() = _Head;
}
数据的插入
实现左旋
_Nodeptr _Y = _Right(_X);
_Right(_X) = _Left(_Y);
if (_Left(_Y) != _Nil) {
_Parent(_Left(_Y)) = _X;
}
_Parent(_Y) = _Parent(_X);
if (_X == _Root()) {
_Root() = _Y;
}
else if (_X == _Left(_Parent(_X))) {
_Left(_Parent(_X)) = _Y;
}
else {
_Right(_Parent(_X)) = _Y;
}
_Left(_Y) = _X;
_Parent(_X) = _Y;
}
实现右旋
void _Rrotate(_Nodeptr _X) {
_Nodeptr _Y = _Left(_X);
_Left(_X) = _Right(_Y);
if (_Right(_Y) != _Nil) {
_Parent(_Right(_Y)) = _X;
}
_Parent(_Y) = _Parent(_X);
if (_X == _Root())
_Root() = _Y;
else if (_X == _Right(_Parent(_X))) {
_Right(_Parent(_X)) = _Y;
}
else {
_Left(_Parent(_X)) = _Y;
}
_Right(_Y) = _X;
_Parent(_X) = _Y;
}
红黑树删除
红黑树的删除分为两种情况,情况1是有两个子的情况,情况2最多有一个子树的情况
_Nodeptr _X;
_Nodeptr _Y = (_P++)._Mynode();
_Nodeptr _Z = _Y;
_Y:表示的是要删除的节点,_P表示的是临近_Y的大的节点
_Z:赋值为_Y
情况1:最多有一个子树的情况
- 如果左子树或者右子树为空的情况
if (_Left(_Y) == _Nil) {
_X = _Right(_Y);
}
else if (_Right(_Y) == _Nil) {
_X = _Left(_Y);
}
把_X分别设置为右子树和左子树。
- 然后把_X的父节点,设置为_Y的父节点即可;
_Parent(_X) = _Parent(_Y);
- 特殊情况判断,如果为root()节点
if (_Root() == _Z)
_Root() = _X;
否则就设置_Z的左右子树
else if (_Left(_Parent(_Z)) == _Z) {
_Left(_Parent(_Z)) = _X;
}
else {
_Right(_Parent(_Z)) = _X;
}
- 判断是否为_Lmost
if (_Lmost() != _Z) {
;
}
如果_Z是_Lmost就把_Z的父节点,设置为_Lmost
else if (_Right(_Z) == _Nil) {
_Lmost() = _Parent(_Z);
}
else {
_Lmost() = _Min(_X);
}
_Rmost的判断是同样的。
情况2:有两个子树的情况
_Y = _Min(_Right(_Y));
_X = _Right(_Y);
去_Y的右子树的最小节点,然后把_X置为该节点的右数,_Z是要删除的节点。目的是把_Y这个节点,放到_Z的位置上去;接下来就是要调整,_Z,_Y的左右子树和父节点的指向。
- 更新_Z的左右子树的父节点的指向为_Y
_Parent(_Left(_Z)) = _Y;
- 更新_Y的左子树的指向为_Z的左子树的指向
_Left(_Y) = _Left(_Z);
- 如果恰好_Y==_Right(_Z)
if (_Y == _Right(_Z)) {
_Parent(_X) = _Y;
}
- 否则
_Parent(_X) = _Parent(_Y);
_Left(_Parent(_Y)) = _X;
_Right(_Y) = _Right(_Z);
_Parent(_Right(_Z)) = _Y;
删除后平衡调整
如果删除的是黑色节点,才需要进行平衡调整。
分析左子树的情况:
while (_X != _Root() && _Color(_X) == _Black) {
if (_X == _Left(_Parent(_X))) {
- 获取_X节点的父节点的右子树
_Nodeptr _W = _Right(_Parent(_X));
- 如果_W的颜色是红色
_Color(_W) = _Black;
_Color(_Parent(_X)) = _Red;
_Lrotate(_Parent(_X));
_W = _Right(_Parent(_X));
if (_Color(_Left(_W)) == _Black&&_Color(_Right(_W)==_Black) {
_Color(_W)=_Red;
_X=_Parent(_X);
}
else {
if (_Color(_Right(_W)) == _Black) {
_Color(_Left(_W))=_Black;
_Color(_W)=_Red;
_Rrotate(_W);
_W=_Right(_Parent(_X));
}
_Color(_W) = _Color(_Parent(_X));
_Color(_Parent(_X)) = _Black;
_Color(_Right(_W)) = _Black;
_Lrotate(_Parent(_X));
break;
}