前面分别介绍红黑树的理论知识和红黑树的C语言实现。本章是红黑树的C++实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章。
目录
1. 红黑树的介绍
2. 红黑树的C++实现(代码说明)
3. 红黑树的C++实现(完整源码)
4. 红黑树的C++测试程序
转载请注明出处:红黑树(四)之 C++的实现 - 如果天空不死 - 博客园
更多内容:数据结构与算法系列 目录
(01) 红黑树(一)之 原理和算法详细介绍
(02) 红黑树(二)之 C语言的实现
(03) 红黑树(三)之 Linux内核中红黑树的经典实现
(04) 红黑树(四)之 C++的实现
(05) 红黑树(五)之 Java的实现
(06) 红黑树(六)之 参考资料
红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树。
红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。
除了具备该特性之外,红黑树还包括许多额外的信息。
红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black)。
红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
关于它的特性,需要注意的是:
第一,特性(3)中的叶子节点,是只为空(NIL或null)的节点。
第二,特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
红黑树示意图如下:
红黑树的基本操作是添加、删除和旋转。在对红黑树进行添加或删除后,会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。下面分别对红黑树的基本操作进行介绍。
1. 基本定义
enum RBTColor{RED, BLACK};template RBTNode是红黑树的节点类,而RBTree对应是红黑树的操作实现类。在RBTree中包含了根节点mRoot和红黑树的相关API。 2. 左旋 对x进行左旋,意味着"将x变成一个左节点"。 左旋的实现代码(C++语言) /* * 对红黑树的节点(x)进行左旋转** 左旋示意图(对节点x进行左旋):* px px* / /* x y * / \ --(左旋)--> / \ #* lx y x ry * / \ / \* ly ry lx ly ***/ 3. 右旋 对y进行左旋,意味着"将y变成一个右节点"。 右旋的实现代码(C++语言) /* * 对红黑树的节点(y)进行右旋转** 右旋示意图(对节点y进行左旋):* py py* / /* y x * / \ --(右旋)--> / \ #* x ry lx y * / \ / \ #* lx rx rx ry* */ 4. 添加 将一个节点插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过"旋转和重新着色"等一系列操作来修正该树,使之重新成为一颗红黑树。详细描述如下: 第二步:将插入的节点着色为"红色"。 第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。 /* * 将结点插入到红黑树中** 参数说明:* root 红黑树的根结点* node 插入的结点 // 对应《算法导论》中的node*/ 内部接口 -- insert(root, node)的作用是将"node"节点插入到红黑树中。其中,root是根,node是被插入节点。 /** 红黑树插入修正函数** 在向红黑树中插入节点之后(失去平衡),再调用该函数;* 目的是将它重新塑造成一颗红黑树。** 参数说明:* root 红黑树的根* node 插入的结点 // 对应《算法导论》中的z*/ insertFixUp(root, node)的作用是对应"上面所讲的第三步"。它是一个内部接口。 5. 删除操作 将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。详细描述如下: 第一步:将红黑树当作一颗二叉查找树,将节点删除。 第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。 /* * 删除结点(node),并返回被删除的结点** 参数说明:* root 红黑树的根结点* node 删除的结点*/ 内部接口 -- remove(root, node)的作用是将"node"节点插入到红黑树中。其中,root是根,node是被插入节点。 /** 红黑树删除修正函数** 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;* 目的是将它重新塑造成一颗红黑树。** 参数说明:* root 红黑树的根* node 待修正的节点*/ removeFixup(root, node, parent)是对应"上面所讲的第三步"。它是一个内部接口。 下面是红黑树实现的完整代码和相应的测试程序。 红黑树的实现文件(RBTree.h) View Code 红黑树的测试文件(RBTreeTest.cpp) View Code 测试程序已经包含在相应的实现文件(MaxHeap.cpp)中了,这里就不再重复说明。下面是测试程序的运行结果: == 原始数据: 10 40 30 60 90 70 20 50 80 C++红黑树 一、红黑树的概念及性质 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的 注:AVL树是严格平衡的二叉搜索树,左右子树高度不超过1;红黑树是近似平衡,最长路径不超过最短路径的二倍 示图: 红黑树的性质: 根节点是黑色的 如果一个节点是红色的,则它的两个孩子结点是黑色的 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 每个NIL结点都是黑色的(此处的结点指的是空结点)(该条规则确定了路径条数) 为什么红黑树就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍: 示图: 二、红黑树结点的定义 实现代码: pair RBTreeNode(const pair 在节点的定义中为什么要将节点的默认颜色给成红色的: 如果默认颜色为红,那么在插入中插入一个红结点,可能新插入结点的父结点为黑色结点则没有影响,也可能新插入结点的父结点为红结点,由于不能存在连续的(父子相连的)红色结点,而对该棵树造成影响 所以默认为红色比较黑色来说是好的 三、红黑树的插入操作 红黑树的插入可分为两步: 新节点插入后检查红黑树的性质是否造到破坏 注意: 当新插入节点的父亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论 因为插入结点的父结点是红色的,说明父结点不是根结点(根结点是黑色的),因此插入结点的祖父结点(父结点的父结点)就一定存在 红黑树调整时具体应该如何调整,主要是看插入结点的叔叔(插入结点的父结点的兄弟结点),根据插入结点叔叔的不同,可将红黑树的调整分为三种情况 注:约定cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点 1、变色处理 示图: 分析: 因为 p 和 u 的颜色变黑,对其子树没有影响,但是 g 的颜色变红,可能存在影响,需要继续向上判断 判断逻辑: 如果 g 是整棵树的子树,如果 g 的父节点是是黑色结点则不用调整,如果是红色结点则需要根据具体情况来做出调整 解决方式: 抽象示图: 动图演示1: 动图演示2: 2、单旋+变色 示图: 分析: 当 u 节点存在且为黑色时,说明 cur 的颜色插入前一定是黑色,进过插入后变色为红色,否则在插入前就存在连续的红色结点,不符合红黑树性质 解决方法: 如果p为g的右孩子,cur为p的右孩子,则进行左单旋转,p变黑,g变红 抽象示图: 动态示图: 动图演示2: 3、双旋+变色 示图: 分析: 解决方式: 如果p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,p旋转后再对g进行左单旋,旋转后将cur变黑,g变红 抽象示图: 动图演示1: 动图演示2: 4、插入实现 pair //查找位置插入节点 //创建链接节点 cur = granparent; granparent->_col = RED; cur->_col = BLACK; cur = granparent; parent->_col = BLACK; cur->_col = BLACK; void RotateL(Node* parent) subR->_left = parent; if (parent == _root) void RotateR(Node* parent) subL->_right = parent; if (parent == _root) 检测其是否满足红黑树的性质 实现代码: bool _IsRBTree(Node* root, int blacknum, int count) 六、红黑树与AVL树的比较 红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数 所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多 附上源码 RBTree.h: #pragma once //颜色 pair RBTreeNode(const pair template void _Destory(Node*& root) _Destory(root->_left); delete root; Node* Find(const K& key) pair //查找位置插入节点 //创建链接节点 //父节点存在且为红,则需要调整(不能存在连续的红色节点) cur = granparent; granparent->_col = RED; cur->_col = BLACK; cur = granparent; parent->_col = BLACK; cur->_col = BLACK; //确保根节点为黑 bool IsRBTree() if (_root->_col == RED) int Blacknum = 0; int i = 0; private: if (root->_col == RED && root->_parent->_col == RED) if (root->_col == BLACK) return _IsRBTree(root->_left, blacknum, count) && _IsRBTree(root->_right, blacknum, count); void RotateL(Node* parent) subR->_left = parent; if (parent == _root) void RotateR(Node* parent) subL->_right = parent; if (parent == _root) private: void TestRBTree() //t.InOrder(); }
class RBTNode{public:RBTColor color; // 颜色T key; // 关键字(键值)RBTNode *left; // 左孩子RBTNode *right; // 右孩子RBTNode *parent; // 父结点RBTNode(T value, RBTColor c, RBTNode *p, RBTNode *l, RBTNode *r):key(value),color(c),parent(),left(l),right(r) {}
};template
class RBTree {private:RBTNode
#define rb_color(r) ((r)->color)
#define rb_is_red(r) ((r)->color==RED)
#define rb_is_black(r) ((r)->color==BLACK)
#define rb_set_black(r) do { (r)->color = BLACK; } while (0)
#define rb_set_red(r) do { (r)->color = RED; } while (0)
#define rb_set_parent(r,p) do { (r)->parent = (p); } while (0)
#define rb_set_color(r,c) do { (r)->color = (c); } while (0)
};
注意:(01) 在实现红黑树API的过程中,我重载了许多函数。重载的原因,一是因为有的API是内部接口,有的是外部接口;二是为了让结构更加清晰。
(02) 由于C++的实现是在上一篇介绍的"C语言"实现基础上移植而来,在该代码中,保留了一些C语言的特性;例如(宏定义)。
template
void RBTree
{// 设置x的右孩子为yRBTNode
}
template
void RBTree
{// 设置x是当前节点的左孩子。RBTNode
}
第一步: 将红黑树当作一颗二叉查找树,将节点插入。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
好吧?那接下来,我们就来想方设法的旋转以及重新着色,使这颗树重新成为红黑树!
为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。o(∩∩)o...哈哈
第二步中,将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
对于"特性(1)",显然不会违背了。因为我们已经将它涂成红色了。
对于"特性(2)",显然也不会违背。在第一步中,我们是将红黑树当作二叉查找树,然后执行的插入操作。而根据二叉查找数的特点,插入操作不会改变根节点。所以,根节点仍然是黑色。
对于"特性(3)",显然不会违背了。这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
对于"特性(4)",是有可能违背的!
那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。
添加操作的实现代码(C++语言)
template
void RBTree
{RBTNode
}/* * 将结点(key为节点键值)插入到红黑树中** 参数说明:* tree 红黑树的根结点* key 插入结点的键值*/
template
void RBTree
{RBTNode
}
外部接口 -- insert(key)的作用是将"key"添加到红黑树中。
添加修正操作的实现代码(C++语言)
template
void RBTree
{RBTNode
}
这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。
因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
删除操作的实现代码(C++语言)
template
void RBTree
{RBTNode
}/* * 删除红黑树中键值为key的节点** 参数说明:* tree 红黑树的根结点*/
template
void RBTree
{RBTNode
}
外部接口 -- remove(key)删除红黑树中键值为key的节点。
删除修正操作的实现代码(C++语言)
template
void RBTree
{RBTNode
} 红黑树的C++实现(完整源码)
(1) 除了上面所说的"左旋"、"右旋"、"添加"、"删除"等基本操作之后,还实现了"遍历"、"查找"、"打印"、"最小值"、"最大值"、"创建"、"销毁"等接口。
(2) 函数接口大多分为内部接口和外部接口。内部接口是private函数,外部接口则是public函数。
(3) 测试代码中提供了"插入"和"删除"动作的检测开关。默认是关闭的,打开方法可以参考"代码中的说明"。建议在打开开关后,在草稿上自己动手绘制一下红黑树。红黑树的C++测试程序
== 前序遍历: 30 10 20 60 40 50 80 70 90
== 中序遍历: 10 20 30 40 50 60 70 80 90
== 后序遍历: 20 10 50 40 70 90 80 60 30
== 最小值: 10
== 最大值: 90
== 树的详细信息:
30(B) is root
10(B) is 30's left child
20(R) is 10's right child
60(R) is 30's right child
40(B) is 60's left child
50(R) is 40's right child
80(B) is 60's right child
70(R) is 80's left child
90(R) is 80's right child 上面的内容错误较多,看一下下面这个文章给出已给较为准确的实现
零、前言
一、红黑树的概念及性质
二、红黑树结点的定义
三、红黑树的插入操作
1、变色处理
2、单旋+变色
3、双旋+变色
4、插入实现
四、红黑树的验证
五、红黑树的删除
六、红黑树与**AVL**树的比较
零、前言
本章继AVL树后继续讲解学习C++中另一个二叉搜索树–红黑树
概念:
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black
每个结点不是红色就是黑色
红黑树第三条性质说明红黑树不能存在连续(父子相连)的红结点,可以存在连续的黑结点,又由于第四条性质每个路径上的黑结点个数都相同 ,所以对于最短路径来说一定是都是黑色结点,对于最长路径来说一定是黑色红色相间的路径,所以最长路径不超过最短路径长度的二倍
对于红黑树来说以颜色来代替AVL树的平衡因子的作用,除此之外在定义上没有什么区别
enum Colour//颜色
{
RED,
BLACK,
};
template
struct RBTreeNode
{
RBTreeNode
RBTreeNode
RBTreeNode
Colour _col;
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _col(RED)
{}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
注:此处采用枚举来表示,当然也可以使用其他方式
如果默认颜色为黑,那么在插入中插入一个黑结点一定会让该路径上的黑结点数量加1,从而与其他路径上黑结点数量造成不一致,而一定会影响该棵红黑树
红黑树是在二叉搜索树的基础上加上其平衡限制条件,当违反限制条件时就需要做出相应的调整
按照二叉搜索的树规则插入新节点
因为新节点的默认颜色是红色,如果其父亲节点的颜色是黑色,则没有违反红黑树任何性质,则不需要调整
情况一: cur为红,p为红,g为黑,u存在且为红
为了让该以 g 为根的子树不存在连续的红色结点,并且不增加路径上的黑色结点,我们让 g 的颜色变红,让 u 和 p 的颜色变黑,各路径上黑色结点数量没变,以此达到红黑树的性质
如果 g 就是该棵树的根那么最后需要将 g 的颜色变黑
将p,u改为黑,g改为红,然后把g当成cur,继续向上调整
情况二: cur为红,p为红,g为黑,u不存在**/u**为黑
当 u 节点不存在时,说明cur一定是插入节点,如果 cur 不是新插入节点,那么在插入前时 cur 一定是黑色结点(由黑色变红),则插入前就不符合路径上黑结点数量相同的性质
如果p为g的左孩子,cur为p的左孩子,则进行右单旋转,p变黑,g变红
情况三: cur为红,p为红,g为黑,u不存在**/u**为黑
这里显然一次旋转也无法让达到红黑树平衡,由此就需要进行两次旋转
如果p为g的左孩子,cur为p的右孩子,则针对p做左单旋转,p旋转后再对g进行右单旋,旋转后将cur变黑,g变红
插入主体实现:
{
//空树的情况
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return make_pair(_root, true);
}
Node* cur = _root, * parent = _root;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_kv.first
parent = cur;
cur = cur->_right;
}
else
{
return make_pair(cur, false);
}
}
cur = new Node(kv);
Node* newnode = cur;
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//父节点存在且为红,则需要调整(不能存在连续的红色节点)
while (parent && parent->_col == RED)
{
//此时当前节点一定有祖父节点
Node* granparent = parent->_parent;
//具体调整情况主要看叔叔节点
//分左右讨论
if (parent == granparent->_left)
{
Node* uncle = granparent->_right;
//情况1:叔叔节点存在且为红
if (uncle && uncle->_col == RED)
{
//修改颜色,继续向上检查
granparent->_col = RED;
parent->_col = uncle->_col = BLACK;
parent = cur->_parent;
}
else//情况2和3:叔叔节点不存在 或者存在且为黑
{
//单旋(三代节点为斜线)+变色
if (cur == parent->_left)
{
RotateR(granparent);
parent->_col = BLACK;
}
else//双旋(三代节点为折线)+变色
{
RotateL(parent);
RotateR(granparent);
granparent->_col = RED;
}
//旋转后不需再向上调整了
break;
}
}
else//parent=grandparent->right
{
Node* uncle = granparent->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
granparent->_col = RED;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(granparent);
granparent->_col = RED;
}
else
{
RotateR(parent);
RotateL(granparent);
granparent->_col = RED;
}
break;
}
}
}
//确保根节点为黑
_root->_col = BLACK;
return make_pair(newnode, true);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
旋转实现:
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentP = parent->_parent;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
parent->_parent = subR;
{
_root = subR;
subR->_parent = nullptr;
}
else
{
subR->_parent = parentP;
if (parentP->_left == parent)
{
parentP->_left = subR;
}
else
{
parentP->_right = subR;
}
}
}
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentP = parent->_parent;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
parent->_parent = subL;
{
_root = subL;
subL->_parent = nullptr;
}
else
{
subL->_parent = parentP;
if (parentP->_left == parent)
{
parentP->_left = subL;
}
else
{
parentP->_right = subL;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
四、红黑树的验证
红黑树的检测分为两步:
检测其是否满足二叉搜索树(中序遍历是否为有序序列)
bool IsRBTree()
{
//空树
if (_root == nullptr)
{
return true;
}
//根节点为黑色
if (_root->_col == RED)
{
cout <<"根节点为红色" <
}
//黑色结点数量各路径上相同
//先走一条得到基准值
int Blacknum &#61; 0;
Node* cur &#61; _root;
while (cur)
{
if (cur->_col &#61;&#61; BLACK)
Blacknum&#43;&#43;;
cur &#61; cur->_left;
}
//检查子树
int i &#61; 0;
return _IsRBTree(_root, Blacknum, i);
}
{
//递归到空节点
if (root &#61;&#61; nullptr)
{
if (blacknum &#61;&#61; count)
return true;
cout <<"各路径上黑色节点个数不同" <
}
//子节点为红则检查父节点是否为红&#xff08;通过父节点检查子节点会遇到空节点&#xff09;
if (root->_col &#61;&#61; RED && root->_parent->_col &#61;&#61; RED)
{
cout <<"存在连续红色节点" <
}
//计数黑结点
if (root->_col &#61;&#61; BLACK)
count&#43;&#43;;
//递归左右子树
return _IsRBTree(root->_left, blacknum, count) && _IsRBTree(root->_right, blacknum, count);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
五、红黑树的删除
红黑树的删除不做讲解&#xff0c;有兴趣可参考&#xff1a;《算法导论》或者《STL源码剖析》
http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html
http://blog.csdn.net/chenhuajie123/article/details/11951777
分析总结&#xff1a;
红黑树和AVL树都是高效的平衡二叉树&#xff0c;增删改查的时间复杂度都是O( )
#include
#include
using namespace std;
enum Colour
{
RED,
BLACK,
};
template
struct RBTreeNode
{
RBTreeNode
RBTreeNode
RBTreeNode
Colour _col;
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _col(RED)
{}
};
class RBTree
{
typedef RBTreeNode
public:
RBTree()
:_root(nullptr)
{
}
{
if (root &#61;&#61; nullptr)
return;
_Destory(root->_right);
root &#61; nullptr;
}
~RBTree()
{
_Destory(_root);
}
{
Node* cur &#61; _root;
while (cur)
{
if (cur->_kv.first > key)
{
cur &#61; cur->_left;
}
else if (cur->_kv.first
cur &#61; cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
{
//空树的情况
if (_root &#61;&#61; nullptr)
{
_root &#61; new Node(kv);
_root->_col &#61; BLACK;
return make_pair(_root, true);
}
Node* cur &#61; _root, * parent &#61; _root;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent &#61; cur;
cur &#61; cur->_left;
}
else if (cur->_kv.first
parent &#61; cur;
cur &#61; cur->_right;
}
else
{
return make_pair(cur, false);
}
}
cur &#61; new Node(kv);
Node* newnode &#61; cur;
if (parent->_kv.first > kv.first)
{
parent->_left &#61; cur;
}
else
{
parent->_right &#61; cur;
}
cur->_parent &#61; parent;
while (parent && parent->_col &#61;&#61; RED)
{
//此时当前节点一定有祖父节点
Node* granparent &#61; parent->_parent;
//具体调整情况主要看叔叔节点
//分左右讨论
if (parent &#61;&#61; granparent->_left)
{
Node* uncle &#61; granparent->_right;
//情况1&#xff1a;叔叔节点存在且为红
if (uncle && uncle->_col &#61;&#61; RED)
{
//修改颜色&#xff0c;继续向上检查
granparent->_col &#61; RED;
parent->_col &#61; uncle->_col &#61; BLACK;
parent &#61; cur->_parent;
}
else//情况2和3&#xff1a;叔叔节点不存在 或者存在且为黑
{
//单旋(三代节点为斜线)&#43;变色
if (cur &#61;&#61; parent->_left)
{
RotateR(granparent);
parent->_col &#61; BLACK;
}
else//双旋(三代节点为折线)&#43;变色
{
RotateL(parent);
RotateR(granparent);
granparent->_col &#61; RED;
}
//旋转后不需再向上调整了
break;
}
}
else//parent&#61;grandparent->right
{
Node* uncle &#61; granparent->_left;
if (uncle && uncle->_col &#61;&#61; RED)
{
parent->_col &#61; uncle->_col &#61; BLACK;
granparent->_col &#61; RED;
parent &#61; cur->_parent;
}
else
{
if (cur &#61;&#61; parent->_right)
{
RotateL(granparent);
granparent->_col &#61; RED;
}
else
{
RotateR(parent);
RotateL(granparent);
granparent->_col &#61; RED;
}
break;
}
}
}
_root->_col &#61; BLACK;
return make_pair(newnode, true);
}
{
if (_root &#61;&#61; nullptr)
{
return true;
}
{
cout <<"根节点为红色" <
}
Node* cur &#61; _root;
while (cur)
{
if (cur->_col &#61;&#61; BLACK)
Blacknum&#43;&#43;;
cur &#61; cur->_left;
}
return _IsRBTree(_root, Blacknum, i);
}
bool _IsRBTree(Node* root, int blacknum, int count)
{
if (root &#61;&#61; nullptr)
{
if (blacknum &#61;&#61; count)
return true;
cout <<"各路径上黑色节点个数不同" <
}
{
cout <<"存在连续红色节点" <
}
count&#43;&#43;;
}
{
Node* subR &#61; parent->_right;
Node* subRL &#61; subR->_left;
Node* parentP &#61; parent->_parent;
parent->_right &#61; subRL;
if (subRL)
{
subRL->_parent &#61; parent;
}
parent->_parent &#61; subR;
{
_root &#61; subR;
subR->_parent &#61; nullptr;
}
else
{
subR->_parent &#61; parentP;
if (parentP->_left &#61;&#61; parent)
{
parentP->_left &#61; subR;
}
else
{
parentP->_right &#61; subR;
}
}
}
{
Node* subL &#61; parent->_left;
Node* subLR &#61; subL->_right;
Node* parentP &#61; parent->_parent;
parent->_left &#61; subLR;
if (subLR)
{
subLR->_parent &#61; parent;
}
parent->_parent &#61; subL;
{
_root &#61; subL;
subL->_parent &#61; nullptr;
}
else
{
subL->_parent &#61; parentP;
if (parentP->_left &#61;&#61; parent)
{
parentP->_left &#61; subL;
}
else
{
parentP->_right &#61; subL;
}
}
}
Node* _root;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include "RBTree.h"
#include
#include
#include
#include
{
//int a[] &#61; { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
//int a[] &#61; { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
RBTree
int n &#61; 1000000;
srand(time(0));
for (int i &#61; 0; i
int e &#61; rand();
t.Insert(make_pair(e, e));
}
cout <
void test_RBTree()
{
int a[] &#61; { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
RBTree
for (auto e : a)
{
t.Insert(make_pair(e, e));
cout <
int main()
{
test_RBTree();
TestRBTree();
return 0;
}