实用指南:R&B:红黑树的节奏与平衡

目录

一、红黑树的概念

 二、红黑树节点的定义

三、红黑树的结构

四、红黑树的Insert

RotateL左单旋,RotateR右单旋

五、红黑树的IsBalance

六、红黑树的Height

七、RBTree.h


一、红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的

  •  红黑树的性质 
  1. 每个结点不是红色就是黑色 
  2. 根节点是黑色的  
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的  
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点  
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

最重要的规则 :

红节点不能相连

每条路上的黑节点数量一致

(在后续的作图中,小编一般忽略空节点)

 二、红黑树节点的定义

树节点  : 

template
struct RBTreeNode
{
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
pair _kv;
Colour _col;
RBTreeNode(const pair kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED)
{
}
};

  • 红黑树的节点的结构采用三叉链的形式实现,并且新增了类型为Colour的颜色 _col这个成员变量用于表示节点的颜色
  • 红黑树要求节点的颜色不是红色就是黑色,那么我们采用枚举enum的方式进行定义Colour用于存储节点的颜色红色RED和黑色BLACK即可,这样每个节点中都存一个颜色_col ,就确保了红黑树的节点的颜色不是红色就是黑色
  • _col赋值为红色 ,一般节点默认给红色

三、红黑树的结构

#pragma once
#include
using namespace std;
enum Colour
{
RED,
BLACK
};
template
struct RBTreeNode
{
pair _kv;
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
Colour _col;
RBTreeNode(const pair kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_col(RED)
{}
};
template
class RBTree
{
typedef RBTreeNode Node;
public:
RBTree()
:_root(nullptr)
{}
private:
Node* _root;
};
  • ps : 红黑树里就一个root 节点,红黑树是靠一个个树节点链接起来的,每个节点链接起来才是红黑树,而红黑树就像是一个root节点+增删查改树节点

四、红黑树的Insert

Insert 

  • 找位置
  • 链接节点
  • 变颜色

红黑树Insert的 变颜色 情况 总思路 (insert的节点cur 为红,找到该在的位置链接得到parent的后面):

一 :

  • parent为黑    ->   直接插入

二 :

1.parent 为红(grandfather 肯定为黑 )

(parent在grandfather的左边)

  • uncle存在且为红 -> uncle , parent变黑 ,grandfather 变红,在把grandfather 当作新节点,向上处理
if (parent == grandfather->_left)//parent在grandfather的左边
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)//如果uncle存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
_root->_col = BLACK;
}

简图

总图

  • uncle不存在||uncle 为黑(这两种情况都是一样的旋转 + 变色 ,uncle存不存在不影响旋转 + 变色 ,而关键是cur在parent的右还是左,ta 会影响旋转,是双旋还是单旋)

i : cur在parent的左 -> 右单旋(grandfather) + parent变黑 , grandfather变红

else//uncle存在且为红||uncle不存在
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
}

ii :cur在parent的右 -> 左单旋(parent) -> 右单旋(grandfather) +  cur变黑 , grandfather变红

else //cur = parent->_right
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;

(parent在grandfather的右边)

  • uncle存在且为红 -> uncle , parent变黑 ,grandfather 变红,在把grandfather 当作新节点,向上处理
else //parent == grandfather->_right   parent在grandfather的右边
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)//如果uncle存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
_root->_col = BLACK;
}
}

  • uncle不存在||uncle 为黑(这两种情况都是一样的旋转 + 变色 ,uncle存不存在不影响旋转 + 变色 ,而关键是cur在parent的右还是左,ta 会影响旋转,是双旋还是单旋)

i : cur在parent的右 -> 左单旋(grandfather) + parent变黑 , grandfather变红

else//uncle存在且为红||uncle不存在
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
}

ii :cur在parent的左 -> 右单旋(parent) -> 左单旋(grandfather) +  cur变黑 , grandfather变红

else //cur = parent->_left
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;

代码:

bool insert(const pair& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col=BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;//cur  : 找到kv该插入的位置
while (cur)
{
if (cur->_kv.first _right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
return false;
}
cur = new Node(kv);
if (parent->_kv.first _right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;// 也就是说每一个节点的_parent都存着它的parent指针
//开始变颜色
while (parent && parent->_col == RED)//parent存在且为红
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)//parent在grandfather的左边
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)//如果uncle存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
_root->_col = BLACK;
}
else//uncle存在且为红||uncle不存在
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else //cur = parent->_right
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else //parent == grandfather->_right   parent在grandfather的右边
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)//如果uncle存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
_root->_col = BLACK;
}
else//uncle存在且为红||uncle不存在
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else //cur = parent->_left
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
return true;
}
  • parent一定要变黑,如果有uncle  ,若uncle为红,则让parent和uncle一起变黑,把同层的节点都减少一个红,看似是减少了两个红节点,但是我们只需要在节点上面的分支处把黑改成红,那么不同分支都会增加一个红节点,就算改到根节点为红,最后再把根节点置为黑,在每一个节点都增加了一个黑节点,都不影响
  • 红黑树的插入,关键看uncle
  • uncle存在,且为红则变色加继续向上更新
  • 如果uncle不存在或者uncle存在且为黑,那么旋转加变色,其中旋转是通过位置关系来判断的,怎样的位置关系对应怎样的旋转

关于旋转,可移步到小编关于AVL树的讲解点击这里

RotateL左单旋,RotateR右单旋
private:
void RotateL(Node* parent)
{
_rotatesum++;
Node* ppnode = parent->_parent;
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
cur->_parent = ppnode;
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
}
}
void RotateR(Node* parent)
{
_rotatesum++;
Node* ppnode = parent->_parent;
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
cur->_parent = ppnode;
}
else
{
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
}

五、红黑树的IsBalance

  • IsBalance需要递归判断是否达到了红黑树的平衡,那么就需要控制传入的节点进行递归,所以在函数参数中必须要有一个root指针,那么用户在调用IsBalance的时候最初就需要传入_root红黑树的根节点,由于_root是私有成员,在类外又拿不到_root,除非在书写一个get_root函数在外部才能获取_root红黑树的根节点,这里没有必要这样写,我们可以通过函数重载,IsBalance()和IsBalance(Node* root)构成函数重载
  • bool IsBalance()
    {
    return IsBalance(_root);
    }
    bool IsBalance(Node* root)
  • IsBalance()通过IsBalance(_root)去调用IsBalance(Node* root)即可,我们在IsBalance(Node* root)中进行递归红黑树判断即可

红黑树的这五点性质

  • 每个节点不是红色就是黑色
  • 根节点是黑色的
  • 如果一个节点是红色的,那么它的两个孩子节点是黑色的,即任何路径上没有连续的红色节点
  • 根节点的每条路径上的黑色节点的数量都相同
  • 每个叶子节点都是黑色的(这里的叶子节点是空节点),即在红黑树中每个NIL的节点都是黑色的长路径的节点数量不超过最短路径的节点数量的2倍

ps : 

  1. 检查parent的颜色和cur的颜色是否都是红的(都是红就出问题了)
  2. 检查每个路径的黑色节点的个数是否相同
bool CheckColour(Node* root, int blacksum, int leftpathblack)
{
if (root == nullptr)
{
if (blacksum != leftpathblack)
{
cout _parent;
if (parent && parent->_col == RED && root->_col == RED)
{
cout _kv.first _col == BLACK)
blacksum++;
return CheckColour(root->_left, blacksum, leftpathblack) &&
CheckColour(root->_right, blacksum, leftpathblack);
}
bool IsBalance()
{
return IsBalance(_root);
}
bool IsBalance(Node* root)
{
if (root == nullptr)
return true;
if (root->_col != BLACK)
{
cout _col == BLACK)
leftpathbalck++;
cur = cur->_left;
}
return CheckColour(root, 0, leftpathbalck);
}

test一下:

#include
#include "RBTree.h"
using namespace std;
int main()
{
int arr[] = { 2,4,7,12,54,72,24,51,41,10,25 };
RBTree rbt;
for (auto e : arr)
{
rbt.Insert(make_pair(e, e));
cout << e << ':' << rbt.IsBalance() << endl;
}
return 0;
}

六、红黑树的Height

这里的高度计算的方式和AVL树是一样的

int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int heightL = Height(root->_left);
int heightR = Height(root->_right);
return  heightL > heightR ? heightL + 1 : heightR + 1;
}

七、RBTree.h

#pragma once
#include
using namespace std;
enum Colour
{
RED,
BLACK
};
template
struct RBTreeNode
{
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
pair _kv;
Colour _col;
RBTreeNode(const pair kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED)
{
}
};
template
struct RBTree
{
typedef RBTreeNode Node;
public:
bool Insert(const pair& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;//cur  : 找到kv该插入的位置
while (cur)
{
if (cur->_kv.first _right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
return false;
}
cur = new Node(kv);
if (parent->_kv.first _right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;// 也就是说每一个节点的_parent都存着它的parent指针
//开始变颜色
while (parent && parent->_col == RED)//parent存在且为红
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)//parent在grandfather的左边
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)//如果uncle存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
_root->_col = BLACK;
}
else//uncle存在且为红||uncle不存在
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else //cur = parent->_right
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else //parent == grandfather->_right   parent在grandfather的右边
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)//如果uncle存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
_root->_col = BLACK;
}
else//uncle存在且为红||uncle不存在
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else //cur = parent->_left
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
return true;
}
int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int heightL = Height(root->_left);
int heightR = Height(root->_right);
return  heightL > heightR ? heightL + 1 : heightR + 1;
}
bool CheckColour(Node* root, int blacksum, int leftpathblack)
{
if (root == nullptr)
{
if (blacksum != leftpathblack)
{
cout _parent;
if (parent && parent->_col == RED && root->_col == RED)
{
cout _kv.first _col == BLACK)
blacksum++;
return CheckColour(root->_left, blacksum, leftpathblack) &&
CheckColour(root->_right, blacksum, leftpathblack);
}
bool IsBalance()
{
return IsBalance(_root);
}
bool IsBalance(Node* root)
{
if (root == nullptr)
return true;
if (root->_col != BLACK)
{
cout _col == BLACK)
leftpathbalck++;
cur = cur->_left;
}
return CheckColour(root, 0, leftpathbalck);
}
private:
void RotateL(Node* parent)
{
_rotatesum++;
Node* ppnode = parent->_parent;
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
cur->_parent = ppnode;
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
}
}
void RotateR(Node* parent)
{
_rotatesum++;
Node* ppnode = parent->_parent;
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
cur->_parent = ppnode;
}
else
{
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
}
public:
int _rotatesum = 0;
private:
Node* _root=nullptr;
};
posted @ 2025-09-25 15:02  wzzkaifa  阅读(33)  评论(0)    收藏  举报