红黑树 R-B Tree
R-B Tree,全称 Red-Black Tree 又称为 红黑树,它是一种特殊的二叉查找树,红黑树的每个节点都有存储位表示节点的颜色,可以是红Red 或者 黑Black
红黑树是相对平衡的二叉树
特性
1.每个节点或者是黑色或者是红色
2.根节点是黑色
3.每个叶子节点(NIL)是黑色,这里叶子节点是为空 NIL 或者 NULL 的叶子节点
4.如果一个节点是红色的,则它的子节点必须是黑色的
5.从一个节点到该节点的子孙节点的所有路径上包含相同数据的黑节点
应用
红黑树的应用比较广泛,主要是用它来存储有序的数据。它的时间复杂度是 O(logn),效率非常高
例如:Java 集合中 TreeSet 和 TreeMap ,C++ STL 中的set、map以及 Linux 虚拟内存管理都是通过红黑树实现的
基本操作 左旋 右旋
红黑树的基本操作是添加、删除。在对红黑树进行添加或者删除后,都会用到旋转方法,为什么呢?因为添加或者删除红黑树中的节点后,红黑树就发生了变化,可能不满足红黑树的 5条性值,也就不再是以可红黑树
而是以可普通的树。通过旋转,可以使得这颗树重新成为红黑树。旋转的目的是让树保持红黑树的特性
1.左旋
对x进行左旋,意味着"将x变成一个左节点",理解左旋之后,看看下面一个更鲜明的例子。你可以先不看右边的结果,自己尝试一下。
2.右旋
总结:
01.左旋 和右旋是相对的两个概念,原理类似
02.发现左旋与右旋是对称的,无论左旋还是右旋,被旋转的树,在旋转前是二叉查找树,在旋转后还是二叉查找树
左旋:x左旋,意味着将 x的右节点设为x的父节点,x将变为 左节点
z
x/
/ \ --(左旋)-->x
y z/y
右旋:对x 右旋,意味着 将 x 的左节点设为父节点,x 将变为一个右节点
y
x \/ \ --(右旋)-->x
y z \
z
C 代码
#include#include
#define RED 0
#define BLACK 1
//定义红黑树结点
typedef structRBTreeNode
{char color;//颜色
int key;//值
struct RBTreeNode *lchild;//左孩子
struct RBTreeNode *rchild;//右孩子
struct RBTreeNode *parent;//父结点
}Node,*RBTree;//定义红黑树根结点
typedef structrb_root
{
Node*node;
} RBRoot;//创建红黑树,返回红黑树的根
RBRoot*creat_rbtree()
{
RBRoot*root=(RBRoot*)malloc(sizeof(RBRoot));//定义根结点,并分配空间
root->node=NULL;//初始化
returnroot;
}//新建一个结点
Node* creat_rbtree_node(int key,Node *parent,Node *lchild,Node *rchild)
{
Node*p;
p=(Node*)malloc(sizeof(Node));
p->key=key;
p->lchild=lchild;
p->rchild=rchild;
p->color=BLACK;returnp;
}//左旋
void rbtree_left_rotate(RBRoot *root,Node *x)
{
Node*y=x->rchild;//设置x的右结点等于y//首先,先找到y的左孩子,它最终被x收养为右孩子
x->rchild=y->lchild;if (y->lchild!=NULL)
y->lchild->parent =x;
y->parent=x->parent;//x->rchild=y->lchild;//y->lchild->parent=x;//y缺了左孩子,x成为y的左孩子
if(x->parent==NULL)//当x为根结点的时候
{
root->node=y;//将y设为根结点
}else//当x不是根节点的时候
{//y->parent=x->parent;//y接替x做别人的儿子
if(x->parent->lchild==x) //要确定y是做的左孩子还是右孩子
{
x->parent->lchild=y;
}else{
x->parent->rchild=y;
}
}
y->lchild=x;//x就位
x->parent=y;//printf("(对关键字%d进行左旋)",x->key);
}//右旋
void rbtree_right_rotate(RBRoot *root,Node *y)
{
Node*x=y->lchild;
y->lchild=x->rchild;//找到x的右孩子,它最终被y收养为左孩子
if(x->rchild!=NULL)
{
x->rchild->parent=y;
}
x->parent=y->parent;//此时x的右孩子是空的,y来当x的右孩子
if(y->parent==NULL)//如果y为根结点
{
root->node=x;//将x设为根节点
}else//当y不是根节点的时候
{//y->parent=x->parent;//x接替y做别人的儿子
if(y->parent->rchild==y) //要确定x是做的左孩子还是右孩子
{
y->parent->rchild=x;
}else{
y->parent->lchild=x;
}
}
x->rchild=y;//y就位
y->parent=x;//printf("(对关键字%d进行右旋)",y->key);
}//插入修正
void rbtree_insert_fixup(RBRoot *root, Node *node)
{
Node*parent, *gparent;//若父节点存在,并且父节点的颜色是红色
while ((parent = node->parent) && (parent->color==RED))
{
gparent= parent->parent;//若“父节点”是“祖父节点的左孩子”
if (parent == gparent->lchild)
{//Case 1条件:叔叔节点是红色
{
Node*uncle = gparent->rchild;if (uncle && uncle->color==RED)
{//父、叔变黑,爷变红,对爷进行判断
uncle->color=BLACK;
parent->color=BLACK;
gparent->color=RED;
node=gparent;continue;
}
}//Case 2条件:叔叔是黑色,且当前节点是右孩子
if (parent->rchild ==node)
{
Node*tmp;
rbtree_left_rotate(root, parent);//父左旋
tmp =parent;
parent=node;
node=tmp;
}//Case 3条件:叔叔是黑色,且当前节点是左孩子。
parent->color=BLACK;
gparent->color=RED;
rbtree_right_rotate(root, gparent);
}else//若“z的父节点”是“z的祖父节点的右孩子”
{//Case 1条件:叔叔节点是红色
{
Node*uncle = gparent->lchild;if (uncle && (uncle->color==RED))
{
uncle->color=BLACK;
parent->color=BLACK;
gparent->color=RED;
node=gparent;continue;
}
}//Case 2条件:叔叔是黑色,且当前节点是左孩子
if (parent->lchild ==node)
{
Node*tmp;
rbtree_right_rotate(root, parent);
tmp=parent;
parent=node;
node=tmp;
}//Case 3条件:叔叔是黑色,且当前节点是右孩子。
parent->color=BLACK;
gparent->color=RED;
rbtree_left_rotate(root, gparent);
}
}//将根节点设为黑色
root->node->color=BLACK;//printf("对关键字%d进行插入修正",node->key);
}//插入
void rbtree_insert(RBRoot *root,Node *node)
{
Node*y=NULL;
Node*x=root->node;while(x!=NULL)//x为叶子结点跳出循环
{
y=x;if(x->key>node->key)
{
x=x->lchild;
}else{
x=x->rchild;
}
}
node->parent=y;if(y!=NULL)
{if(node->keykey)
{
y->lchild=node;
}else{
y->rchild=node;
}
}else{
root->node=node;//若y为NULL,说明树为空,则将node设为根节点
}
node->color=RED;//将颜色设为红色//插入修正
rbtree_insert_fixup(root, node);
}int insert_rbtree(RBRoot *root,intkey)
{
Node*node;//新建一个结点
node=creat_rbtree_node(key,NULL,NULL,NULL);if(node==NULL) return -1;elserbtree_insert(root,node);return 0;
}/** 中序遍历"红黑树"*/
voidinorder(RBTree tree)
{if(tree !=NULL)
{
inorder(tree->lchild);
printf("%d", tree->key);if(tree->color==0)
{
printf("(RED)");
}else{
printf("(BLACK)");
}
inorder(tree->rchild);
}
}void inorder_rbtree(RBRoot *root)
{if(root)
inorder(root->node);
}intmain()
{int a[10]={5,3,6,7,4,1,2,8,10,9};int i;//计数器
intkey;int n=sizeof(a)/sizeof(int);
printf("**********原始数据**********\n");for(i=0;i
{
printf("%d",a[i]);
}
printf("\n");//下面开始创建红黑树
RBRoot *root=NULL;//首先创建红黑树的根
root=creat_rbtree();for(i=0;i
{
printf("== 添加节点: %d\n", a[i]);
insert_rbtree(root,a[i]);
printf("== 中序遍历:");
inorder_rbtree(root);
printf("\n");
}
printf("==向红黑树中插入一个值:");
scanf("%d",&key);
insert_rbtree(root,key);
printf("\n== 成功插入后的中序遍历:");
inorder_rbtree(root);
printf("\n");return 0;
}