研究契机
本学期学校开设数据结构课程,在力所能及范围内研究了几个问题,以供自己复习参考。
红黑树特征
红黑树(Red-Black Tree)是具有如下特征的二元查找树:
1.每一个结点或者是红色或者是黑色。
2.每个叶结点都是黑色。
3.如果一个结点是红色,那么它的两个孩子结点都是黑色。
4.从任何一个结点到所有以该结点为子树的叶结点的简单路径上拥有相同的黑色结点。如下图就是一个红黑树的实例:
一般叶结点不存放数据,作为哨兵用来表示已经到达叶结点。
对于红黑树我们可以证明如下定理:任何一个具有n 个内部结点的红黑树其高度至多为2log(n+1)。该定理决定了红黑树可以保证查找时间复杂性一定时O(logn)(具有和平衡树类似的性质)。
解题实践
由于本学期开设的课程设计中有相关题目,以学校课程设计中的题目为例:
①设计并实现Red-Black Tree 的ADT,该ADT 包括Tree 的组织存储以及其上的基本操作:包括初始化,查找,插入和删除等。并分析基本操作的时间复杂性。
②实现Red-Black Tree ADT 的基本操作演示(要求应用图形界面)。
③演示实例为依次插入A L G O R I T H M 并依同样的次序删除。
算法思想
根据题目要求,我们首先要实现红黑树的基本操作,包括初始化,查找,插入和删除等。而在之前,我们要设计红黑树输的基本数据结构,用结构体RBNodee表示,里有elementType类型的data存储结点元素,int类型的color代表该结点的颜色,初始值为RBNode_Red,RBNode类型的parent指向其父亲结点、lChild指向其左孩子结点,rChild指向其右孩子结点。
在实现具体操作前,我们先来了解一下红黑树的左旋和右旋,无论是左旋还是右旋都是对两个结点进行的操作,而在传值时,我们传其中父亲结点的值,左旋传入x,对x和x的右孩子y进行操作,右旋时传入y,对y和y的左孩子进行操作,注意:左旋和右旋时,进行操作的两个结点颜色互换
接下来考虑对红黑树各个操作的实现:
-
首先是红黑树的初始化,只需新建一个结点,将结点值赋给data,color改为RBNode_Black即可,其时间复杂度为o(1)。
-
然后是红黑树中元素的查找,首先将要插入的元素与根结点进行比较,若小于根结点,向左查找,若大于根结点,向右查找,找到位置,若未找到,返回NULL,其时间复杂度为o(log2n)。
-
红黑树的插入,规定p为操作结点的父结点,s为操作结点的叔叔结点,pp为操作结点的爷爷结点,我们首先要找到要插入的位置,规定我们插入的位置一定是一个叶子结点,而直接插入可能会破坏红黑树的性质,需分情况进行调整,且情况很多,比较复杂,并且需要用到递归,其时间复杂度为o(log2n)。
-
而红黑树的结点的删除则更加复杂,规定,删除结点为R,删除结点父结点为P,删除结点的兄弟结点为S,兄弟结点的左孩子为SL,右孩子为SR。我们首先需要找到要删除的结点,需要用到红黑树的查找函数,没有找到要删除的结点,直接返回,找到要删除的结点,需要分情况进行讨论,其时间复杂度为o(log2n)。
-
最后需要考虑的是如何打印红黑树,首先我们应求出红黑树的高度h。然后按照先序遍历的方式打印红黑树,我们设置红黑树每层之间的差距为dy,完全二叉树的叶子结点与父结点的横向差距为dx,先序遍历时传入一个参数i存储该节点的层数,再传入两个参数x,y代表要输出的坐标位置,i初始值为1,x,y初始值为根结点的位置,即函数原型为void printTree(RBNode* T,int i, int x, int y),打印出根结点后依次为printTree(T->lChild, i + 1, x - d,y + dy); printTree(T->rChild, i + 1, x + d,y + dy);其中d = dx * 2h-i-1。
调试过程
在实现算法过程中,第一次出错是在插入算法写好,进行测试时。但是在检查算法后,并没有发现问题,对算法进行手工也没有问题,最终通过调试发现,在进行左旋右旋操作时,对根结点进行旋转之后,没有将根结点的指针进行改变。最终的解决方法是,在对根结点进行旋转时,并没有真正的旋转,而是将两个要旋转结点的值进行交换,然后进行一些操作,使之达到旋转的目的。
#define RBNode_Red 0
#define RBNode_Black 1
#include<stdio.h>
typedef char elementType;
typedef struct lBNode //红黑树数据结构
{
elementType data; //存储数据
int color = 0; //初始颜色为红色
struct lBNode* lChild, * rChild;
struct lBNode* parent;
}RBNode, * RBTree;
void modulation(RBNode* s); //红黑树调整函数
void modulation2(RBNode* p); //红黑树删除时的调整函数
RBNode* pioneer(RBNode* p); //求出p中序遍历的先驱结点
void initialRBTree(RBNode*& T, elementType x) //初始化红黑树的根结点
{
T = new RBNode;
T->data = x;
T->color = RBNode_Black; //将根结点设为黑色
T->lChild = NULL;
T->rChild = NULL;
T->parent = NULL;
}
void insertNode(RBNode* T, elementType x) //向红黑树中插入结点
{
if (T->data < x) //根结点小于x时
{
if (T->rChild == NULL) //若右子树不存在,即为插入位置
{
RBNode* p = new RBNode;
p->data = x;
p->lChild = NULL;
p->rChild = NULL;
T->rChild = p;
p->parent = T;
modulation(p); //对新插入的结点进行调整
}
else //若右子树存在,则递归插入
{
insertNode(T->rChild, x);
}
}
else //根结点大于x时
{
if (T->lChild == NULL)
{
RBNode* p = new RBNode;
p->data = x;
p->lChild = NULL;
p->rChild = NULL;
T->lChild = p;
p->parent = T;
modulation(p); //对新插入的结点进行调整
}
else
{
insertNode(T->lChild, x);
}
}
}
void leftRotate(RBNode* s) //红黑树左旋
{
if (s->parent == NULL) //s为根结点时
{
RBNode* p = s->rChild;
elementType x;
int color;
//此处进行的操作没有改变根结点的位置,即不是真正意义上的左旋
x = s->data;
s->data = p->data;
p->data = x;
color = s->color;
s->color = p->color;
p->color = color;
s->rChild = p->rChild;
s->rChild->parent = s;
p->rChild = p->lChild;
//判断根结点是否存在左孩子
if (s->lChild)
{
p->lChild = s->lChild;
p->lChild->parent = p;
}
s->lChild = p;
}
else //s不为根结点时
{
RBNode* p = s->rChild;
p->parent = s->parent;
//判断s为其父节点的哪个孩子结点
if (s->parent->lChild == s)
{
s->parent->lChild = p;
}
else
{
s->parent->rChild = p;
}
s->parent = p;
s->rChild = p->lChild;
if (s->rChild) s->rChild->parent = s;
p->lChild = s;
}
}
void rightRotate(RBNode* s) //红黑树右旋
{
if (s->parent == NULL) //s为根结点时
{
RBNode* p = s->lChild;
elementType x;
int color;
//此处进行的操作没有改变根结点的位置,即不是真正意义上的左旋
x = s->data;
s->data = p->data;
p->data = x;
color = s->color;
s->color = p->color;
p->color = color;
s->lChild = p->lChild;
s->lChild->parent = s;
p->lChild = p->rChild;
//判断根结点是否有右孩子
if (s->rChild)
{
p->rChild = s->rChild;
p->rChild->parent = p;
}
s->rChild = p;
}
else //s不为根结点时
{
RBNode* p = s->lChild;
p->parent = s->parent;
//判断s为其父节点的哪个孩子结点
if (s->parent->lChild == s)
{
s->parent->lChild = p;
}
else
{
s->parent->rChild = p;
}
s->parent = p;
s->lChild = p->rChild;
if (s->lChild) s->lChild->parent = s;
p->rChild = s;
}
}
void modulation(RBNode* s) //红黑树调整函数
{
if (s->parent->color == RBNode_Black) //s的父亲结点为黑色时不用调整
{
return;
}
RBNode* t = s->parent;
RBNode* p = t->parent;
if (p->lChild == t) //如果s的父结点为其爷爷结点的左孩子
{
if (p->rChild == NULL || p->rChild->color == RBNode_Black) //如果s的叔叔结点为黑色色,或不存在叔叔结点
{
if (t->lChild == s) //如果s为其父结点的左孩子
{
//变色进行右旋
t->color = RBNode_Black;
p->color = RBNode_Red;
rightRotate(p);
}
else //若s为父结点的右孩子
{
//先左旋后变色进行右旋
leftRotate(t);
s->color = RBNode_Black;
p->color = RBNode_Red;
rightRotate(p);
}
}
else //若叔叔结点为红色
{
t->color = RBNode_Black;
p->rChild->color = RBNode_Black;
if (p->parent != NULL) //如果爷爷结点不为为根结点,继续调整
{
p->color = RBNode_Red;
modulation(p);
}
}
}
else //如果s的父结点为其爷爷结点的右孩子,一切对称
{
if (p->lChild == NULL || p->lChild->color == RBNode_Black) //如果s的叔叔结点为黑色,或不存在叔叔结点
{
if (t->rChild == s) //如果s为其父结点的右孩子
{
//变色进行左旋
t->color = RBNode_Black;
p->color = RBNode_Red;
leftRotate(p);
}
else //若s为父结点的左孩子
{
//先右旋后变色进行左旋
rightRotate(t);
s->color = RBNode_Black;
p->color = RBNode_Red;
leftRotate(p);
}
}
else //若叔叔结点为红色
{
t->color = RBNode_Black;
p->lChild->color = RBNode_Black;
if (p->parent != NULL) //如果爷爷结点不为为根结点,继续调整
{
p->color = RBNode_Red;
modulation(p);
}
}
}
}
//void printTree(RBNode* T, int i)
//{ //t代表遍历结点的位置,m为最后一个结点所在位置,i代表所在层次
// int j;
// if (T != NULL) {
// printTree(T->rChild, i + 1); //访问右子树
//
// for (j = 0; j < i - 1; ++j) printf("\t");
// printf("%c", T->data);
// if (T->color == RBNode_Red)
// {
// printf("R\n");
// }
// else
// {
// printf("B\n");
// }
//
// printTree(T->lChild, i + 1); //访问左子树
// }
//}
RBNode* searchNode(RBNode* T, elementType x) //查找函数,查找x所在结点
{
if (T == NULL)
{
return NULL;
}
if (T->data > x) //结点元素大于x,向左查找
{
return searchNode(T->lChild, x);
}
else if (T->data < x) //结点元素小于x,向右查找
{
return searchNode(T->rChild, x);
}
else
{
return T;
}
}
void deleteNode(RBNode*& p) //删除结点p
{
if (p->lChild == NULL && p->rChild == NULL) //如果所删除的结点没有子节点
{
if (p->color == RBNode_Red) //当p为红色时,直接删除
{
if (p->parent->lChild == p)
{
p->parent->lChild = NULL;
}
else
{
p->parent->rChild = NULL;
}
delete p;
}
else //当p为黑色时
{
if (p->parent == NULL) //若p为根结点
{
delete p;
p = NULL;
}
else
{
modulation2(p); //对黑色要删除的结点进行调整
if (p->parent->lChild == p)
{
p->parent->lChild = NULL;
}
else
{
p->parent->rChild = NULL;
}
delete p;
}
}
}
else if (p->lChild == NULL) //若P只有右孩子结点
{
RBNode* son = p->rChild;
//如果p为根结点
if (p->parent == NULL)
{
p->data = son->data;
p->rChild = NULL;
delete son;
}
else
{
if (p->parent->lChild == p)
{
p->parent->lChild = son;
}
else
{
p->parent->rChild = son;
}
son->color = RBNode_Black;
son->parent = p->parent;
delete p;
}
}
else if (p->rChild == NULL) //若只有左孩子结点
{
RBNode* son = p->lChild;
//如果p为根结点
if (p->parent == NULL)
{
p->data = son->data;
p->lChild = NULL;
delete son;
}
else
{
if (p->parent->lChild == p)
{
p->parent->lChild = son;
}
else
{
p->parent->rChild = son;
}
son->color = RBNode_Black;
son->parent = p->parent;
delete p;
}
}
//如果p既有左孩子又有右孩子
else
{
//找到p的前驱结点
RBNode* pion = pioneer(p);
p->data = pion->data;
deleteNode(pion);
}
}
void deleteNode(RBNode*& T, elementType x)
{
//找到要删除的结点
RBNode* p = searchNode(T, x);
if (p == NULL)
{
return;
}
//若p为根结点,且没有孩子结点
if (p->parent == NULL && p->lChild == NULL && p->rChild == NULL)
{
deleteNode(T);
}
else
{
deleteNode(p);
}
}
void modulation2(RBNode* p) //对所删除结点为黑色的叶子结点进行调整
{
RBNode* bro;
if (p->parent == NULL)
{
return;
}
if (p->parent->lChild == p) //如果p为其父结点的左孩子
{
bro = p->parent->rChild;
if (bro->color == RBNode_Black) //如果兄弟结点为黑色
{
if (bro->rChild && bro->rChild->color == RBNode_Red) //若其有右孩子,且为红色
{
bro->rChild->color = RBNode_Black;
bro->color = bro->parent->color;
bro->parent->color = RBNode_Black;
leftRotate(bro->parent);
}
else if (bro->lChild && bro->lChild->color == RBNode_Red) //若其有左孩子其为红色
{
bro->lChild->color = RBNode_Black;
bro->color = RBNode_Red;
rightRotate(bro);
//转换成上面情况,继续调整
modulation2(p);
}
else //如果其孩子没有红色结点
{
if (p->parent->color == RBNode_Red)
{
p->parent->color = RBNode_Black;
bro->color = RBNode_Red;
}
else
{
bro->color = RBNode_Red;
modulation2(p->parent);
}
}
}
else //如果兄弟结点为红色
{
bro->color = RBNode_Black;
bro->parent->color = RBNode_Red;
leftRotate(bro->parent);
modulation2(p);
}
}
else //如果p为父结点的右孩子,一切对称
{
bro = p->parent->lChild;
if (bro->color == RBNode_Black) //如果兄弟结点为黑色
{
if (bro->lChild && bro->lChild->color == RBNode_Red) //若其有左孩子,且为红色
{
bro->lChild->color = RBNode_Black;
bro->color = bro->parent->color;
bro->parent->color = RBNode_Black;
rightRotate(bro->parent);
}
else if (bro->rChild && bro->rChild->color == RBNode_Red) //若其有右孩子,且为红色
{
bro->rChild->color = RBNode_Black;
bro->color = RBNode_Red;
leftRotate(bro);
//转换成上面情况,继续调整
modulation2(p);
}
else //如果其孩子没有红色结点
{
if (p->parent == NULL)
{
return;
}
if (p->parent->color == RBNode_Red)
{
p->parent->color = RBNode_Black;
bro->color = RBNode_Red;
}
else
{
bro->color = RBNode_Red;
modulation2(p->parent);
}
}
}
else //如果兄弟结点为红色
{
bro->color = RBNode_Black;
bro->parent->color = RBNode_Red;
rightRotate(bro->parent);
modulation2(p);
}
}
}
RBNode* pioneer(RBNode* p) //求出p中序遍历的先驱结点
{
RBNode* pion; //表示要返回的结点
pion = p->lChild;
while (pion->rChild)
{
pion = pion->rChild;
}
return pion;
}
void deleteTree(RBNode* T) //删除整棵树
{
if (T)
{
//后序遍历
deleteTree(T->lChild);
deleteTree(T->rChild);
delete T;
}
}