继上篇在平衡中追寻高度:探秘AVL树的自我调节之美,我们继续讨论红黑树的相关知识。 在计算机科学的广阔天地中,数据结构如同乐器,各具特色,共同奏响高效算法的乐章。在众多自平衡树中,红黑树以其独特的结构与高效的性能,成为了实现平衡的典范。本博文将深入探讨红黑树的原理、特点及其在实际应用中的表现,揭示这一数据结构如何在动态数据操作中保持高效与稳定。
红黑树是一种自平衡的二叉搜索树,其中每个节点都被标记为红色或黑色。通过这些颜色标记以及特定的规则,红黑树能够在插入和删除节点时保持平衡,从而确保基本操作的效率。

这些特性使得红黑树在需要动态数据存储和高效查找的应用中非常有用,比如标准库中的std::map和std::set。
template<class K, class V>
class RBTreeNode {
public:
RBTreeNode<K, V>* left;
RBTreeNode<K, V>* right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv; //结点中存的键值对
color _col; //结点的颜色
RBTreeNode(const& pair<K, V> kv = pair<K, V>(), Color color = RED)
:_kv(kv)
,left(nullptr)
,right(nullptr)
,_parent(nullptr)
,_col(color)
{}
};RED。这是因为,如果新插入结点的颜色是BLACK,那意味着当前路径上新增了一个黑色结点,为了保证红黑树的第5条性质,我们要对这颗红黑树其他的所有路径进行检查,可见新插入结点如果默认是BLACK,会存在着牵一发而动全身的影响。而让新插入结点默认是RED则不会出现这样的结果。假如新插入结点的 parent 恰好是 BLACK,那这次插入就没有什么问题。如果新插入结点是 parent 是 RED,此时需要对这颗红黑树稍作调整。template<class K, class V>
class RBTree {
typedef RBTreeNode<K, V> Node;
public:
// 成员函数...
private:
Node* _root;
};// 左单旋
void RotateL(Node* parent) {
Node* cur = parent->right;
Node* curleft = cur->left;
parent->right = curleft;
cur->left = parent;
if (curleft) curleft->_parent = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (parent == _root) {
_root = cur;
cur->_parent = nullptr;
}
else {
if (ppnode->left == parent) ppnode->left = cur;
else ppnode->right = cur;
cur->_parent = ppnode;
}
}
// 右单旋
void RotateR(Node* parent) {
Node* cur = parent->left;
Node* curright = cur->right;
parent->left = curright;
cur->right = parent;
if (curright) curright->_parent = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (parent == _root) {
_root = cur;
cur->_parent = nullptr;
}
else {
if (ppnode->left == parent) ppnode->left = cur;
else ppnode->right = cur;
cur->_parent = ppnode;
}
}红黑树是在二叉搜索树的基础上加上平衡限制条件,因此红黑树的插入可以分为两步:
因为新结点的默认颜色是 RED,因此:如果其双亲结点的颜色是 BLACK,没有违反红黑树任何性质,则不需要调整;但是当新插入节点的双亲结点颜色为 RED 时,就违反了性质四不能有连在一起的红色结点,此时需要对红黑树分情况来讨论:
约定:cur为当前结点,parent 为父结点,grandfather 为祖父结点,uncle 为叔叔结点。如果 parent 为红那 grandfather 一定为黑。所以当前唯一不确定的就是 uncle,主要分以下三种情况:
uncle结点存在且为红uncle和grandfather结点也要一起更新。




uncle结点不存在或者存在且为黑




bool insert(const pr& kv) {
if (!_root) {
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
// 定义当前节点cur
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; // 不可能出现相等的情况插入
}
// 创建新的Node实例cur,并将它插入到适当的位置
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) {
Node* grandfather = parent->_parent;
// 父亲是祖父的左孩子
if (grandfather->left == parent) {
// 叔叔存在且为红
if (uncle && uncle->_col == RED) {
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续向上处理
cur = grandfather;
parent = cur->_parent;
}
// 叔叔不存在或者存在且为黑
else {
if (cur == parent->left) {
RotateR(grandparent);
parent->_col = BLACK;
grandfather->_col = RED;
}
else {
RotateL(cur);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
}
}
// 父亲是祖父的右孩子
else {
// 叔叔存在且为红
if (uncle && uncle->_col == RED) {
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续向上处理
cur = grandfather;
parent = cur->_parent;
}
// 叔叔不存在或者存在且为黑
else {
if (cur == parent->right) {
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else {
RotateR(cur);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
}
}
}
return true;
}红黑树的检测分为两步:
成员函数:
// 调用判断平衡的函数
bool IsBalance() {
return IsBalance(_root);
}
// 判断平衡
bool IsBalance(Node* root) {
if (!root) return true;
if (root->_col != BLACK) return false;
// 随便找一条路径,这里我们选择最左路径
int benchmark = 0;
Node* cur = _root;
while (cur) {
if (cur->_col == BLACK) {
++benchmark;
}
cur = cur->left;
}
return CheckColor(root, 0, benchmark);
}
// 主要检查有无连续的红色结点
bool CheckColor(Node* root, int blacknum, int benchmark) {
if (!root) {
if (blacknum != benchmark) {
return false;
}
return true;
}
if (root->_col == BLACK) ++blacknum;
if (root->_col == RED && root->_parent && root->_parent->_col == RED) {
cout << root->_kv.first << "出现连续红色节点" << endl;
return false;
}
return CheckColor(root->left, blacknum, benchmark) && CheckColor(root->right, blacknum, benchmark);
}
// 调用中序遍历
void InOrder() {
return InOrder(_root);
}
// 中序遍历
void InOrder(Node* root) {
if (!root) return;
InOrder(root->left);
cout << root->_kv.first << " ";
InOrder(root->right);
}测试代码:
#include "RBTree.h"
int main() {
int a[] = { 1, 5, 7, 16, 25, 36, 44, 55,6, 32, 9 };
RBTree<int, int> rbt;
for (auto e : a) {
rbt.insert(make_pair(e,e));
rbt.InOrder();
cout << endl;
if (rbt.IsBalance()) {
cout << "插入成功" << endl;
}
else {
cout << "插入失败" << endl;
}
}
return 0;
}
AVL树和红黑树是两种常用的自平衡二叉搜索树,各有优缺点。以下是它们在性能方面的比较:
std::map 和 std::set 实现。总结
红黑树不仅是一种数据结构,更是一种智慧的象征。它巧妙地平衡了插入、删除与查找操作之间的矛盾,为开发者在处理复杂数据时提供了强大的支持。通过对红黑树的深入理解,我们不仅能提升自身的编程能力,更能在数据处理的艺术中,找到更为优雅的解决方案。希望本篇博文能够为你在数据结构的探索之路上,带来新的启发与思考。
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,17的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是17前进的动力!