目录
红黑树的概念
红黑树的性质
红黑树的调整情况
红黑树的模拟实现
枚举类型的定义
红黑树节点的定义
插入函数的实现
旋转函数的实现
左旋
右旋
自检函数的实现
红黑树类
红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的
红黑树的性质
每个结点不是红色就是黑色
根节点是黑色的
如果一个节点是红色的,则它的两个孩子结点是黑色的
对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
红黑树的调整情况
-
新节点插入为红色:
新节点默认着色为红色。
-
父节点为黑色:
如果新插入节点的父节点是黑色,那么通常只需要进行简单的颜色调整即可,因为黑色节点不会破坏红黑树的性质。
-
父节点为红色:
这是需要重点关注的情况,因为红色父节点可能违反红黑树的性质。
叔节点也为红色: 如果叔节点(父节点的兄弟节点)也是红色,则将父节点和叔节点都变为黑色,并将祖父节点(父节点的父节点)变为红色。接着,将祖父节点作为新的当前节点,继续向上检查是否破坏了性质。 -叔节点为黑色或不存在: 根据新节点是父节点的左子节点还是右子节点,以及父节点是祖父节点的左子节点还是右子节点,需要进行不同的调整。
父节点是左子节点:
新节点是父节点的右子节点:将父节点进行左旋操作,然后重新从父节点开始着色。 新节点是父节点的左子节点:将父节点变为黑色,将祖父节点变为红色,然后对祖父节点进行右旋操作。
父节点是右子节点:
新节点是父节点的左子节点:将父节点进行右旋操作,然后重新从父节点开始着色。
红黑树的模拟实现
枚举类型的定义
enum Color
{
RED,
BLACK
};
红黑树节点的定义
-
成员有:左孩子指针、有孩子指针、父节点指针、一个键值对、颜色(枚举类型)
-
构造函数:将三个指针置空,颜色默认为红色,由外面传递的键值对作为值
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _color;
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _color(RED)
{}
};
插入函数的实现
-
函数参数:
const pair<K, V>& kv:要插入到红黑树中的键值对。
-
检查根节点是否为空:
if (_root == nullptr):如果红黑树为空(即根节点为空),则创建一个新的节点作为根节点,并设置其颜色为黑色。然后返回true表示插入成功。
-
查找插入位置:
使用parent和cur指针遍历树,查找插入位置。parent用于记录当前节点的父节点,cur用于遍历树。根据键值对的大小关系,将cur指针移动到正确的子树中。如果在遍历过程中发现键值对已经存在于树中(即cur->_kv.first == kv.first),则直接返回false表示插入失败。
-
创建新节点并插入:
创建一个新的节点cur,并设置其值为kv,颜色为红色。根据之前找到的parent节点的键值对大小关系,将新节点插入到父节点的左子树或右子树中,并更新节点的父节点指针。
-
调整红黑树:
由于新插入的节点颜色是红色,可能会破坏红黑树的性质。因此,需要进行一系列调整操作来恢复红黑树的性质。while (parent && parent->_color == RED)循环用于检查父节点的颜色,如果父节点是红色的,则需要进行调整。 根据父节点是祖父节点的左子节点还是右子节点,以及新节点是父节点的左子节点还是右子节点,选择不同的调整策略。如果父节点和叔叔节点(祖父节点的另一个子节点)都是红色的,则将父节点和叔叔节点的颜色改为黑色,并将祖父节点的颜色改为红色。然后更新cur和parent指针,继续检查新的父节点。 如果叔叔节点是黑色的,则根据新节点是父节点的左子节点还是右子节点,选择进行左旋或右旋操作,以及可能的颜色调整。在循环结束后,确保根节点的颜色是黑色,这是红黑树的一个基本性质。
-
返回插入结果:
由于插入操作已经成功完成,无论是否进行了调整操作,都返回true表示插入成功。
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_color = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
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->_color = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent && parent->_color == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
else
{
RotateL(parent);
RotateL(grandfather);
cur->_color = BLACK;
grandfather->_color = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
}
else
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_color = BLACK;
grandfather->_color = RED;
}
break;
}
}
}
_root->_color = BLACK;
return true;
}
旋转函数的实现
-
这两个函数RotateL和RotateR分别用于执行红黑树的左旋和右旋操作。在红黑树的插入和删除操作中,当破坏了红黑树的性质时,通常需要通过节点的旋转和颜色调整来恢复树的平衡。
左旋
-
左旋操作通常用于当某个节点的右子节点是红色时。左旋操作将右子节点提升为父节点,并将原父节点降为其左子节点。同时,原父节点的左子节点则成为新父节点的右子节点。
void RotateL(Node* parent)
{
Node* subR = parent->_right; // 右子节点
Node* subRL = subR->_left; // 右子节点的左子节点
parent->_right = subRL; // 原父节点的右子节点指向右子节点的左子节点
if (subRL)
subRL->_parent = parent; // 更新subRL的父节点指针
subR->_left = parent; // 右子节点的左子节点指向原父节点
parent->_parent = subR; // 更新原父节点的父节点指针
Node* parentParent = parent->_parent; // 原父节点的父节点
if (_root == parent) // 如果原父节点是根节点
{
_root = subR; // 更新根节点为右子节点
subR->_parent = nullptr; // 根节点的父节点指针置为null
}
else
{
if (parentParent->_left == parent) // 如果原父节点是其父节点的左子节点
{
parentParent->_left = subR; // 更新父节点的左子节点为右子节点
}
else
{
parentParent->_right = subR; // 如果原父节点是其父节点的右子节点,则更新父节点的右子节点为右子节点
}
subR->_parent = parentParent; // 更新右子节点的父节点指针
}
}
右旋
-
右旋操作与左旋操作相反,通常用于当某个节点的左子节点是红色时。右旋操作将左子节点提升为父节点,并将原父节点降为其右子节点。同时,原父节点的右子节点则成为新父节点的左子节点。
void RotateR(Node* parent)
{
Node* subL = parent->_left; // 左子节点
Node* subLR = subL->_right; // 左子节点的右子节点
parent->_left = subLR; // 原父节点的左子节点指向左子节点的右子节点
if (subLR)
subLR->_parent = parent; // 更新subLR的父节点指针
subL->_right = parent; // 左子节点的右子节点指向原父节点
parent->_parent = subL; // 更新原父节点的父节点指针
Node* parentParent = parent->_parent; // 原父节点的父节点
if (_root == parent) // 如果原父节点是根节点
{
_root = subL; // 更新根节点为左子节点
subL->_parent = nullptr; // 根节点的父节点指针置为null
}
else
{
if (parentParent->_left == parent) // 如果原父节点是其父节点的左子节点
{
parentParent->_left = subL; // 更新父节点的左子节点为左子节点
}
else
{
parentParent->_right = subL; // 如果原父节点是其父节点的右子节点,则更新父节点的右子节点为左子节点
}
subL->_parent = parentParent; // 更新左子节点的父节点指针
}
}
自检函数的实现
-
Check函数
这个函数递归地检查红黑树的每个节点,确保满足红黑树的性质。
首先,它检查传入的节点是否为空(即已经到达了叶子节点)。如果是,它会比较从根节点到该叶子节点的黑色节点数量是否等于refVal(预期值)。如果不等,则打印错误信息并返回false。接着,它检查当前节点是否与其父节点连续为红色,如果是,则打印错误信息并返回false。如果当前节点是黑色,则增加blacknum的计数。最后,它递归地检查左子树和右子树。
-
IsBalance 函数
这个函数用于检查整棵红黑树是否平衡。
首先,它检查根节点是否为空或红色。如果根节点为空,则树是平衡的;如果根节点为红色,则树不平衡。接下来,它计算从根节点到最左侧叶子节点路径上的黑色节点数量,并将此值作为refVal(预期值)。然后,它调用Check函数,从根节点开始,检查整棵树是否满足红黑树的性质,特别是黑色节点数量的要求。
bool Check(Node* root, int blacknum, const int refVal)
{
if (root == nullptr)
{
//cout << balcknum << endl;
if (blacknum != refVal)
{
cout << "存在黑色节点数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_color == RED && root->_parent->_color == RED)
{
cout << "有连续的红色节点" << endl;
return false;
}
if (root->_color == BLACK)
{
++blacknum;
}
return Check(root->_left, blacknum, refVal)
&& Check(root->_right, blacknum, refVal);
}
public:
bool IsBalance()
{
if (_root == nullptr)
return true;
if (_root->_color == RED)
return false;
//参考值
int refVal = 0;
Node* cur = _root;
while (cur)
{
if (cur->_color == BLACK)
{
++refVal;
}
cur = cur->_left;
}
int blacknum = 0;
return Check(_root, blacknum, refVal);
}
红黑树类
-
使用KV模型
-
私有成员为红黑树根节点指针
-
包含插入函数、旋转函数、中序遍历、判断是否是红黑树等
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_color = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
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->_color = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent && parent->_color == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
else
{
RotateL(parent);
RotateL(grandfather);
cur->_color = BLACK;
grandfather->_color = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
}
else
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_color = BLACK;
grandfather->_color = RED;
}
break;
}
}
}
_root->_color = BLACK;
return true;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
subR->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (subRL)
subRL->_parent = parent;
if (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
private:
// 根节点->当前节点这条路径的黑色节点的数量
bool Check(Node* root, int blacknum, const int refVal)
{
if (root == nullptr)
{
//cout << balcknum << endl;
if (blacknum != refVal)
{
cout << "存在黑色节点数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_color == RED && root->_parent->_color == RED)
{
cout << "有连续的红色节点" << endl;
return false;
}
if (root->_color == BLACK)
{
++blacknum;
}
return Check(root->_left, blacknum, refVal)
&& Check(root->_right, blacknum, refVal);
}
public:
bool IsBalance()
{
if (_root == nullptr)
return true;
if (_root->_color == RED)
return false;
//参考值
int refVal = 0;
Node* cur = _root;
while (cur)
{
if (cur->_color == BLACK)
{
++refVal;
}
cur = cur->_left;
}
int blacknum = 0;
return Check(_root, blacknum, refVal);
}
private:
Node* _root = nullptr;
};
}