红黑树的定义:红黑树是一种较为“平衡的”二叉查找树,其每个结点上增加了一个储存颜色的位置,可以是红也可以是黑色的,并且有父亲结点。基本操作时间为O(lgn).
红黑树的性质:1)每个结点或是红的,或是黑的。2)根结点是黑的。 3)每个叶结点(NIL)是黑的。 4)若一个结点是红的,则它的两个儿子都是黑的。5)对每个结点,从该结点到其子孙结点的所有路径上的黑色结点数目相同。
黑高度:从某个结点x出发(不包括该结点)到达一个叶结点的任意一条路径上,黑色结点的个数称为该结点x的黑高度,用bh(x)表示。
引理13.1 一棵有n个内结点的红黑树的高度至多为2lg(n+1).
练习:
13.1-1 使用图13-1a的格式,画出在关键字集合{1,2,....,15}上高度为3的完全二叉查找树。以三种不同方式,向图中加入NIL叶结点并对各结点着色,使所得的红黑树的黑高度分别为2,3,4.
图1表示黑度为4的红黑树,图2表示黑度为3的红黑树,图3表示黑度为2的红黑树。
13.1-2对图13-1中的红黑树,画出调用TREE-INSERT插入关键字36后的结果。结果插入的结点被标为红色,所得的树是否还是一棵红黑树?如果该结点被标为黑色呢?
标为红色后,不是红黑树,不符合性质4. 标为黑色后,不是红黑树,不符合性质5.第3节学习插入函数后,可以知道每插入一个红色结点,可以通过调整使之成为一棵红黑树。
13.1-3 定义松弛红黑树为满足红黑性质1,3,4,5的二叉查找树。换言之,根部可以是红色或是黑色。考虑一棵根是红色的松弛红黑树T。如果将T的根部标为黑色而其他都不变,则所得到的是否还是一棵红黑树?
是。
13.1-4假设将一棵红黑树的每一个红结点“吸收”到它的黑色父结点中,来让红结点的子女变成黑色父结点的子女(忽略关键字的变化)。当一个黑结点的所有红色子女都被吸收后,其可能的度是多少?此结果树的叶子深度怎么样?
度为2时,是左右孩子结点已经都是黑色。度为3时,左或者右孩子结点是红色。 度为4时,左右孩子结点都是红色。 结果树叶子深度都相同。
以下13.1-5~7 都用到了红黑树一个特别的结构,那就是从根结点开始,一层黑一层红,颜色交替,构成了一棵拥有红色结点最多的红黑树。
13.1-5 证明:在一棵红黑树中,从某结点x到其后代叶结点的所有简单路径中,最长的一条是最短一条的至多两倍。
最短:此路径上全是黑色结点。最长:此路径上红黑结点交替出现(红色结点数目=黑色结点数目),又因为不管到最长路径还是到最短路径,黑高(黑色结点数目)都相等。最长:最短=(红+黑):黑=2:1:由此得出论点。
13.1-6 在一棵黑高度为k的红黑树中,内结点最多可能有多少个?最少可能有多少个?
根据引理13.1 至少包含2^k-1个内部结点。至多包含2^(2k)-1个内部结点。(最多就是红黑结点交替出现的情况)
13.1-7 请描述出一棵在n个关键字上构造出来的红黑树,使其中红的内部结点数与黑的内部结点数比值最大。这个比值是多少?具有最小可能比例的树又是怎么样?此比值是多少?
最小可能比值就是所有结点都是黑色,比值为0。 最大可能是红黑结点交替出现,并且最底层是全部是红色结点,比值是2:1.
13.2 旋转
旋转分为:左旋和右旋。
左旋:在某个结点x做左旋时,它的右孩子非空。 右旋:在某个结点x做右旋时,它的左孩子非空。
//两个结点间是由两个指针连接成的 可以说x的孩子结点指向B,同时也是B的父亲结点指向x 由于有父亲结点属性的存在,所以是双向连接
左旋代码如下:
struct Tree*root=NULL;
void LEFT_ROTATE(struct Tree*T,struct Tree*x)
{//左旋转:分三个步骤①②③来叙述旋转代码的。
struct Tree*y=x->right;//设置y结点。
x->right=y->left;//本行代码以及下面的if结构表达的是“y的左孩子成为x的右孩子”。①
if(y!=NULL&&y->left!=NULL)
{
y->left->parent=x;
}
y->parent=x->parent;//本行代码以及下面的if-else结构表达的过程是“y成为该子树新的根”。②
if(x->parent==NULL)
{
root=y;
}
else if(x==x->parent->left)
{
x->parent->left=y;
}
else x->parent->right=y;
y->left=x;//本行代码以及下面一行都表达了“x成为y的左孩子”。③
x->parent=y;
}
13.2-1 写出RIGHT-ROTATE的伪代码。
只需将书中的代码中的right变为left,而left变为right即可。
13.2-2 证明:在一棵有n个结点的二叉查找树中,刚好有n-1种可能的旋转。
因为n个结点有n-1条边,每条边都可以(左或右)旋转,所以有n-1种可能的旋转。
13.2-3设在图13-2的左边一棵树中,a,b,c分别为子树α,β,γ中的任意结点。如果将结点x左旋,则a,b,c深度如何变化?
α上升1层,β不变,γ下降1层。
13.2-4 证明:任何一棵含n个结点的二叉查找树,可以通过O(n)次旋转,转变为另一棵含n个结点的二叉查找树。
每次右(左)旋转,都会使最右(左)链上多一个结点,经过任意n-1次右(左)旋转,任意右(左)总会变成一个单链表。所以任意二叉查找树都能经过O(n)次旋转转换成任意二叉查找树。
13.2-5 如果能够使用一系列的RIGHT-ROTATE调用来把一个二叉查找树T1变为二叉查找树T2,则说T1可以右转成T2。请给出一个两棵树的例子,其中T1不能右转成T2.然后证明如果T1可以右转成T2,则它可以使用O(n^2)次RIGHT-ROTATE调用来右转。
void RB_INSERT_INSERT_FIXUP(struct Tree*T,struct Tree*z)
{
while (z->parent->color==RED)
{
if (z->parent==z->parent->parent->left)
{
struct Tree*y=z->parent->parent->right;//叔结点
if (y->color==RED)//情况一:叔结点为红色
{//给p1,y,p2着色以保持性质5。并且解决了z的父结点和z都是红色结点问题
z->parent->color=BLACK;
y->color=BLACK;
z->parent->parent->color=RED;
z=z->parent->parent;//把z的祖父结点当成新结点z进入下一次循环
}
else
{
if (z==z->parent->right)//情况二:检查z是否是一个右孩子且叔结点为黑色,前提是p1结点不是叶子结点
{//使用一个左旋让情况2转变为情况3
z=z->parent;
LEFT_ROTATE(T,z);//由于进入if语句后可知旋转结点不可能是叶子结点,这样就不用判断z是否是叶子结点了。
}
z->parent->color=BLACK;//情况三:是z是一个左孩子且叔结点为黑色,改变z的父和祖父结点颜色并做一次右旋,以保持性质5
z->parent->parent->color=RED;
RIGHT_ROTATE(T,z->parent->parent);//z的祖父结点不会是叶子结点(由13.3-4可知),所以不用在右旋前做判断。
}
}
else//下面else分支类似于上面,注意到else分支的情况2和情况3所做旋转正好是if分支情况的逆。
{
struct Tree*y=z->parent->parent->left;
if (y->color==RED)
{
z->parent->color=BLACK;
y->color=BLACK;
z->parent->parent->color=RED;
z=z->parent->parent;
}
else
{
if (z==z->parent->left)
{
z=z->parent;
RIGHT_ROTATE(T,z);
}
z->parent->color=BLACK;
z->parent->parent->color=RED;
LEFT_ROTATE(T,z->parent->parent);
}
}
}
root->color=BLACK;//最后给根结点着为黑色。
}
void RB_INSERT(struct Tree*T,struct Tree*z)
{
struct Tree*y=nil;
struct Tree*x=root;
while (x!=nil)
{
y=x;
if (z->keykey)
{
x=x->left;
}
else x=x->right;
}
z->parent=y;
if (y==nil)
{
root=z;
}
else if(z->keykey)
{
y->left=z;
}
else y->right=z;
z->left=nil;//给插入结点左右孩子赋值为空。
z->right=nil;
z->color=RED;//给插入结点着为红色。
RB_INSERT_INSERT_FIXUP(T,z);
}
void RB_DELETE_FIXUP(struct Tree*x)
{
struct Tree*w=NULL;//w是x的兄弟结点
while (x!=root&&x->color==BLACK)//如果x是黑色并且不是根结点,才进行循环。
{//x是一个具有双重颜色的结点,调整的目的是把x的黑色属性向上移动。
if (x==x->parent->left)
{
w=x->parent->right;
if (w->color==RED)//情况一:x的兄弟结点w是红色的。
{//改变w和x.p的颜色+一次旋转使其变为情况二,三,四。
w->color=BLACK;
x->parent->color=RED;
LEFT_ROTATE(x->parent);
w=x->parent->right;
}
if (w->left->color==BLACK&&w->right->color==BLACK)//情况二:x的兄弟结点w是黑色的,而且w的两个子节点都是黑色。
{
w->color=RED;//从x和w上去掉一重黑色。x还是黑色,而w变为红色。
x=x->parent;//x结点向上移动成为新的待调整结点。
}
else
{
if (w->right->color==BLACK)//情况三:x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的。
{//交换w和w.left的颜色+旋转使其转变为情况四。
w->left->color=BLACK;
w->color=RED;
RIGHT_ROTATE(w);
w=x->parent->right;
}
w->color=x->parent->color;//以下是情况四:x的兄弟结点w是黑色的,且w的右孩子是红色的。
x->parent->color=BLACK;//置x.p和w.right为黑色+旋转使其去掉x的额外黑色。
w->right->color=BLACK;
LEFT_ROTATE(x->parent);
x=root;//x成为根结点,结束循环。
}
}
else//以下和上面的if分支类似,不做累述。
{
w=x->parent->left;
if (w->color==RED)
{
w->color=BLACK;
x->parent->color=RED;
RIGHT_ROTATE(x->parent);
w=x->parent->left;
}
if (w->left->color==BLACK&&w->right->color==BLACK)
{
w->color=RED;
x=x->parent;
}
else
{
if (w->left->color==BLACK)
{
w->right->color=BLACK;
w->color=RED;
LEFT_ROTATE(w);
w=x->parent->left;
}
w->color=x->parent->color;
x->parent->color=BLACK;
w->left->color=BLACK;
RIGHT_ROTATE(x->parent);
x=root;
}
}
}x->color=BLACK;
}
void RB_DELETE(struct Tree*z)
{
struct Tree*y=z,*x;//y为待删除或待移动结点
int y_original_color=y->color;//保存y的原始颜色,为做最后的调整做准备。
if (z->left==nil)
{
x=z->right;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上
RB_TRANSPLANT(z,z->right);//把以z.right为根的子树替换以z为根的子树。
}
else if (z->right==nil)
{
x=z->left;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上
RB_TRANSPLANT(z,z->left);//把以z.left为根的子树替换以z为根的子树。
}
else
{
y=ITERATIVE_TREE_MINIMUM(z->right);//找到z.right的后继。
y_original_color=y->color;//y的新的原始结点被重置。
x=y->right;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上
if (y->parent==z)
{
x->parent=y;//由于z的父结点是要删除的结点,所以不能指向它,于是指向y
}
else
{
RB_TRANSPLANT(y,y->right);//把以y.right为根的子树替换以y为根的子树。
y->right=z->right;
y->right->parent=y;
}
RB_TRANSPLANT(z,y);//把以y为根的子树替换以z为根的子树。
y->left=z->left;
y->left->parent=y;
y->color=z->color;//把已经删除的结点颜色赋值给y,保证了y以上的树结构红黑性质不变。
}
if(y_original_color==BLACK) //y的原始颜色为黑色,说明需要调整红黑颜色。
RB_DELETE_FIXUP(x);
}
#include
using namespace std;
#define BLACK 0
#define RED 1
#define Nil -1
#define LEN sizeof(struct Tree)
struct Tree
{
struct Tree*left;
struct Tree*right;
struct Tree*parent;
int key;
int color;
};
struct Tree*root=NULL;
struct Tree*nil=NULL;
void LEFT_ROTATE(struct Tree*x)
{//左旋转:分三个步骤①②③来叙述旋转代码的。
struct Tree*y=x->right;//设置y结点。
x->right=y->left;//本行代码以及下面的if结构表达的是“y的左孩子成为x的右孩子”。①
if(y->left!=nil)
{
y->left->parent=x;
}
y->parent=x->parent;//本行代码以及下面的if-else结构表达的过程是“y成为该子树新的根”。②
if(x->parent==nil)
{
root=y;
}
else if(x==x->parent->left)
{
x->parent->left=y;
}
else x->parent->right=y;
y->left=x;//本行代码以及下面一行都表达了“x成为y的左孩子”。③
x->parent=y;
}
void RIGHT_ROTATE(struct Tree*x)
{//右旋转:分三个步骤①②③来叙述旋转代码的。
struct Tree*y=x->left;//设置y结点。
x->left=y->right;//本行代码以及下面的if结构表达的是“y的左孩子成为x的右孩子”。①
if(y->right!=nil)
{
y->right->parent=x;
}
y->parent=x->parent;//本行代码以及下面的if-else结构表达的过程是“y成为该子树新的根”。②
if(x->parent==nil)
{
root=y;
}
else if(x==x->parent->right)
{
x->parent->right=y;
}
else x->parent->left=y;
y->right=x;//本行代码以及下面一行都表达了“x成为y的左孩子”。③
x->parent=y;
}
void RB_INSERT_INSERT_FIXUP(struct Tree*z)
{
while (z->parent->color==RED)
{
if (z->parent==z->parent->parent->left)
{
struct Tree*y=z->parent->parent->right;//叔结点
if (y->color==RED)//情况一:叔结点为红色
{//给p1,y,p2着色以保持性质5。并且解决了z的父结点和z都是红色结点问题
z->parent->color=BLACK;
y->color=BLACK;
z->parent->parent->color=RED;
z=z->parent->parent;//把z的祖父结点当成新结点z进入下一次循环
}
else
{
if (z==z->parent->right)//情况二:检查z是否是一个右孩子且叔结点为黑色,前提是p1结点不是叶子结点
{//使用一个左旋让情况2转变为情况3
z=z->parent;
LEFT_ROTATE(z);//由于进入if语句后可知旋转结点不可能是叶子结点,这样就不用判断z是否是叶子结点了。
}
z->parent->color=BLACK;//情况三:是z是一个左孩子且叔结点为黑色,改变z的父和祖父结点颜色并做一次右旋,以保持性质5
z->parent->parent->color=RED;
RIGHT_ROTATE(z->parent->parent);//由于p2可能是叶子结点,所以最好还是用一个if判断
}
}
else//下面else分支类似于上面,注意到else分支的情况2和情况3所做旋转正好是if分支情况的逆。
{
struct Tree*y=z->parent->parent->left;
if (y->color==RED)
{
z->parent->color=BLACK;
y->color=BLACK;
z->parent->parent->color=RED;
z=z->parent->parent;
}
else
{
if (z==z->parent->left)
{
z=z->parent;
RIGHT_ROTATE(z);
}
z->parent->color=BLACK;
z->parent->parent->color=RED;
LEFT_ROTATE(z->parent->parent);
}
}
}
root->color=BLACK;//最后给根结点着为黑色。
}
void RB_INSERT(struct Tree*z)
{
struct Tree*y=nil;
struct Tree*x=root;
while (x!=nil)
{
y=x;
if (z->keykey)
{
x=x->left;
}
else x=x->right;
}
z->parent=y;
if (y==nil)
{
root=z;
}
else if(z->keykey)
{
y->left=z;
}
else y->right=z;
z->left=nil;//给插入结点左右孩子赋值为空。
z->right=nil;
z->color=RED;//给插入结点着为红色。
RB_INSERT_INSERT_FIXUP(z);
}
void RB_TRANSPLANT(struct Tree*u,struct Tree*v)
{
if (u->parent==nil)
root=v;
else if(u==u->parent->left)
u->parent->left=v;
else u->parent->right=v;
v->parent=u->parent;
}
//非递归版本的查找二叉查找树的最小值
struct Tree*ITERATIVE_TREE_MINIMUM(struct Tree*x)
{
while (x->left!=nil)
{
x=x->left;
}
return x;
}
//非递归版本的二叉查找树查找函数
struct Tree*ITERATIVE_TREE_SEARCH(struct Tree*x,int k)
{
while (x!=nil&&k!=x->key)
{
if (kkey)
{
x=x->left;
}
else x=x->right;
}
return x;
}
void RB_DELETE_FIXUP(struct Tree*x)
{
struct Tree*w=NULL;//w是x的兄弟结点
while (x!=root&&x->color==BLACK)//如果x是黑色并且不是根结点,才进行循环。
{//x是一个具有双重颜色的结点,调整的目的是把x的黑色属性向上移动。
if (x==x->parent->left)
{
w=x->parent->right;
if (w->color==RED)//情况一:x的兄弟结点w是红色的。
{//改变w和x.p的颜色+一次旋转使其变为情况二,三,四。
w->color=BLACK;
x->parent->color=RED;
LEFT_ROTATE(x->parent);
w=x->parent->right;
}
if (w->left->color==BLACK&&w->right->color==BLACK)//情况二:x的兄弟结点w是黑色的,而且w的两个子节点都是黑色。
{
w->color=RED;//从x和w上去掉一重黑色。x还是黑色,而w变为红色。
x=x->parent;//x结点向上移动成为新的待调整结点。
}
else
{
if (w->right->color==BLACK)//情况三:x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的。
{//交换w和w.left的颜色+旋转使其转变为情况四。
w->left->color=BLACK;
w->color=RED;
RIGHT_ROTATE(w);
w=x->parent->right;
}
w->color=x->parent->color;//以下是情况四:x的兄弟结点w是黑色的,且w的右孩子是红色的。
x->parent->color=BLACK;//置x.p和w.right为黑色+旋转使其去掉x的额外黑色。
w->right->color=BLACK;
LEFT_ROTATE(x->parent);
x=root;//x成为根结点,结束循环。
}
}
else//以下和上面的if分支类似,不做累述。
{
w=x->parent->left;
if (w->color==RED)
{
w->color=BLACK;
x->parent->color=RED;
RIGHT_ROTATE(x->parent);
w=x->parent->left;
}
if (w->left->color==BLACK&&w->right->color==BLACK)
{
w->color=RED;
x=x->parent;
}
else
{
if (w->left->color==BLACK)
{
w->right->color=BLACK;
w->color=RED;
LEFT_ROTATE(w);
w=x->parent->left;
}
w->color=x->parent->color;
x->parent->color=BLACK;
w->left->color=BLACK;
RIGHT_ROTATE(x->parent);
x=root;
}
}
}x->color=BLACK;
}
void RB_DELETE(struct Tree*z)
{
struct Tree*y=z,*x;//y为待删除或待移动结点
int y_original_color=y->color;//保存y的原始颜色,为做最后的调整做准备。
if (z->left==nil)
{
x=z->right;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上
RB_TRANSPLANT(z,z->right);//把以z.right为根的子树替换以z为根的子树。
}
else if (z->right==nil)
{
x=z->left;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上
RB_TRANSPLANT(z,z->left);//把以z.left为根的子树替换以z为根的子树。
}
else
{
y=ITERATIVE_TREE_MINIMUM(z->right);//找到z.right的后继。
y_original_color=y->color;//y的新的原始结点被重置。
x=y->right;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上
if (y->parent==z)
{
x->parent=y;//由于z的父结点是要删除的结点,所以不能指向它,于是指向y
}
else
{
RB_TRANSPLANT(y,y->right);//把以y.right为根的子树替换以y为根的子树。
y->right=z->right;
y->right->parent=y;
}
RB_TRANSPLANT(z,y);//把以y为根的子树替换以z为根的子树。
y->left=z->left;
y->left->parent=y;
y->color=z->color;//把已经删除的结点颜色赋值给y,保证了y以上的树结构红黑性质不变。
}
if(y_original_color==BLACK) //y的原始颜色为黑色,说明需要调整红黑颜色。
RB_DELETE_FIXUP(x);
}
//中序遍历
void InOderTraverse(struct Tree *p)
{
if (p!=nil)
{
InOderTraverse(p->left);
cout<key<<" "<color<<" "<right);
}
}
void main()
{
int array1[6] = {41, 38, 31, 12, 19, 8};
nil=new struct Tree[LEN];
nil->key=Nil;nil->color=BLACK;
root=nil;
int i=0;
struct Tree*ROOT=new struct Tree[LEN];
ROOT->key=array1[i++];
RB_INSERT(ROOT);
root=ROOT;
while (i!=6)
{
struct Tree*z=new struct Tree[LEN];
z->key=array1[i];
RB_INSERT(z);
i++;
}
InOderTraverse(root);
cout<