红黑树的基础是二叉搜索树,如对二叉搜索树的操作不了解,请参考上一篇文章:点击打开链接
红黑树操作有点复杂,请参照相关书籍,耐心研究......
部分图片参考博客:http://www.cnblogs.com/Anker/archive/2013/01/30/2882773.html
红黑树是平衡搜索树的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为Lgn。红黑树在二叉搜索树的基础上结点增加了一个属性color,值为black或者red,即一个节点的属性有:left,right,p,color,key。同时,红黑树使用一个哨兵T_NIL代替以一般搜索树中的NULL,T_NIL也是一个普通的结点,也具有以上5个属性,它的color属性为black,其他属性为任意值。即叶子结点的左右孩子全部指向哨兵,以及根节点的父指针也指向哨兵。
一颗红黑树满足以下性质:
1.每个根节点是红色或黑色;
2.根节点是黑色的;
3.每个叶子结点(哨兵)是黑色的;
4.如果一个节点是红色的,则它的两个子节点都是黑色的。
5.对每个结点出发,到达该结点后代叶节点的路径上均包含相同数目的黑色结点。
黑高的定义:从某个结点出发(不包含该结点)到达一个叶节点的任意一条简单路径上的黑色结点个数称为该结点的黑高。一颗红黑树的黑高也就是根节点的黑高;
红黑树例子:
叶节点与根节点的父结点省略后:
T_NIL=(Node *)malloc(sizeof(Node)); //建立T_NIL结点 T_NIL->p=NULL; T_NIL->left=NULL; T_NIL->right=NULL; T_NIL->key=-1; T_NIL->color=black; //只用它的颜色为黑色,其他属性可以为任意值
介绍插入与删除操作前,必须要了解旋转操作,因为插入和删除操作可能会违反红黑树的性质。旋转分为左旋和右旋,旋转是针对某一个结点做的,而且经过旋转操作后仍然能保持二叉搜索树的性质。
左旋与右旋代码,注意以后代码中不存在NULL,只有T_NIL,因为叶子节点左右节点全部指向哨兵节点T_NIL:
void Left_Rotate(Node **T,Node * x) //左旋 对照上图很容易分析代码 { Node *y=x->right; x->right =y->left; //y的左孩子 作为x的右孩子 if (y->left != T_NIL) y->left->p=x; y->p=x->p; // y父节点指向x的父节点 if(x->p==T_NIL) //若x之前为根节点,则根节点更新 *T=y; else if (x == x->p->left) //若x之前为父节点的左孩子,则左孩子现在是y x->p->left = y; else x->p->right = y; y->left = x; x->p=y; } void Right_Rotate(Node **T,Node * y) //右旋 { Node *x=y->left; y->left =x->right; //x 的右孩子 作为 y的左孩子 if (x->right != T_NIL) x->right->p=y; x->p=y->p; if(y->p==T_NIL) // 若y之前为根节点,根节点更新 *T=x; else if (y == y->p->left) //若y为其父节点的左孩子,则左孩子现在是x y->p->left = x; else y->p->right = x; x->right = y; y->p=x; }
Node *RB_Insert(Node *Root,Node * z) //红黑树插入,返回树的根,Root为树根,z为将要插入的结点 { Node * y=T_NIL; Node * x=Root; while( x != T_NIL) //找到结点z要插入的位置,y作为其父节点 { y=x; if (z->key < x->key) x = x->left; else x = x->right; } z->p = y; if ( y == T_NIL) //插入第一个结点作为根节点的情况 Root = z; else if (z->key < y->key) y->left = z; else y->right = z; Root = RB_Insert_Fixup(Root,z); //插入完毕后,对红黑树的颜色进行修正 return Root; }
Node* RB_Insert_Fixup(Node *T,Node *z) { Node * y=NULL; while( z->p->color == red) //违反了性质4,迭代进行修正 { if (z->p == z->p->p->left) { y = z->p->p->right; if ( y->color == red) // case 1 叔结点为红色 { z->p->color = black; //父节点涂黑 y->color = black; //叔结点涂黑 z->p->p->color = red; //祖结点涂红 z = z->p->p; //向上迭代,更新z的位置 } else if ( z == z->p->right) //case 2 叔结点为黑色且z为双亲的右孩子 { z = z->p; Left_Rotate(&T,z); z->p->color = black; //case2 已转为 case3 继续处理 z->p->p->color = red; Right_Rotate(&T,z->p->p);// while循环终止 } else // case 3 叔结点为黑色且z为双亲的左孩子 { z->p->color = black; z->p->p->color = red; Right_Rotate(&T,z->p->p);// while循环终止 } } else //对称处理 { y = z->p->p->left; if ( y->color == red) // case 1 叔结点为红色 { z->p->color = black; y->color = black; z->p->p->color = red; z = z->p->p; } else if ( z == z->p->left) //case 2 叔结点为黑色且z为双亲的右孩子 { z = z->p; Right_Rotate(&T,z); z->p->color = black; z->p->p->color = red; Left_Rotate(&T,z->p->p);// } else // case 3 { z->p->color = black; z->p->p->color = red; Left_Rotate(&T,z->p->p); } } } T->color = black; //保证不会违反性质2,对根节点涂黑 return T; }
删除操作同二叉树的删除操作也是类似,也是同样分为:少于两个子结点、有两个子结点的情况,红色标注不同;
1.z的子节点数少于两个:(令y指向z的位置)如果结点y颜色为红色直接删除,因为不会破坏红黑的性质;若为y黑色,调用颜色修复函数,并令其子树x代替z的位置,并把颜色也改变成z的颜色。
2.z的子节点数有两个:找到z的后继y,用y代替z的位置,并把y的颜色换成z的颜色,这样不会破坏红黑的性质。但是如果y在之前的位置是黑色,现在由于转移走了,y的右子树x代替了y的位置,此时破坏了这个支树的红黑性质,少了一个黑色结点,需要调用颜色修复函数;
如上图1要删除节点4,找到后继y节点5,用y替换z的位置,并颜色也改为z的颜色。同时y之前的位置用x代替,由于y之前为黑色,违反了红黑树性质,所以对于图2中的x就具有了两种颜色,它本身的颜色红色,代替y的颜色黑色。所以x的颜色看做红黑!
综上所述:删除后我们需要判断y结点的颜色,如果y的颜色为红色,同二叉搜索树一样操作;如果为黑色,需要继续调用颜色修复函数。
首先介绍替代的函数RB_Transplant(T,u,v);
void RB_Transplant(Node **T,Node * u,Node * v) { if (u->p == T_NIL) *T = v; else if (u == u->p->left) u->p->left = v; else u->p->right = v; v->p = u->p; //最后这一步无条件执行,假如v是T_NIL也要执行,因为后面颜色修复的需要 }
删除函数RB_Delete(T,z),同二叉树一样,只是最后一步需要判断y的颜色;
Node * RB_Delete(Node *T ,Node *z) { Node * x =NULL; Node * y =z; enum colors y_original_color = y->color; //记录下删除前z的颜色 if ( z->left == T_NIL) //左子树不存在的情况 { x = z->right; RB_Transplant(&T,z,z->right); } else if ( z->right == T_NIL) //右子树不存在 { x = z->left; RB_Transplant(&T,z,z->left); } else //左右都存在的情况 { y = Tree_Minimum(z->right); //找到后继y y_original_color = y->color; //记录下y转移前的颜色 x = y->right; if ( y->p == z) //如果y是z的子结点 { x->p = y; } else { RB_Transplant(&T,y,y->right); //如果y不是z的子结点,用y的右子树代替y的位置 y->right = z->right; y->right->p = y; } RB_Transplant(&T,z,y); //y替代z的位置 ,不论y是不是T_NIL y->left = z->left; y->left->p = y; y->color = z->color; //把y的颜色改成z的颜色 } if ( y_original_color == black) //判断y的颜色,若为黑色,需要修复 RB_Delete_Fixup(T,x); return T; }
下面详细介绍颜色修复函数RB_Delete_Fixup(T,x),我们注意到输入参数是结点x,因为y的删除或者转移,x代替了y的位置,不论x红色还是黑色,它都承担了两重颜色属性,红黑或者黑黑,这样才能维持红黑树的性质。
如果x之前为红色,现在把它涂成黑色即可代替被删除或转移走的y;
如果x之前为也为黑色,x即为黑黑。下面分情况讨论:
以下情况是x作为其父节点的左孩子的情况,右孩子的情况对称处理
☆情况1:x的兄弟结点w是红色的;
因为x的黑结点数已为2,所以w必有两个黑结点;对x->p做一次旋转,这时x的兄弟结点w就变成了黑色,转变成了情况2,3,4;为了维护红黑的性质5,旋转操作前把x->p变成红色,把w变成黑色;
如图:
☆情况2:x的兄弟结点w是黑色的,而且w的两个子节点都是黑色的;
如图所示,由于x代表了双重黑色,把w着为红色,把x上的黑色上移一个给其父节点,让父节点让具有双重颜色,
如果父节点B本身是黑色的,这时父节点B具有黑黑的属性,继续当做x迭代处理!
如果父节点B本身是红色的,这时父节点B具有红黑的属性,直接退出循环,把父节点B涂成黑色即可!
☆情况3:x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的;
交换w与其左孩子的颜色,对w右旋,很容易分析这样处理后不会影响红黑树的性质,这样转变成了情况4
☆情况4:x的兄弟结点w是黑色的,且w的右孩子是红色的;
如图:把w的颜色换成x->p的颜色(不用管x的父结点是红色还是黑色),把x->p的颜色涂成黑色,把w的左孩子涂成黑色,对x的父节点做一次左旋,可以看到不会改变红黑树的性质。
处理前:
从结点B到α的黑色结点数为2,包括B,因为x代表了双重黑色,简记为α(2),则β(2)γ(2)δ(2)ζ(1)ξ(1)
处理后:
x代表一重黑色,则α(2)β(2)γ(2)δ(2)ζ(1)ξ(1) 退出循环
void RB_Delete_Fixup(Node * T,Node * x) //不详细注释,见分析 { Node *w=NULL; while( x != T && x->color == black) //循环迭代处理 { if ( x == x->p->left ) //x是其父节点的左孩子 { w = x->p->right; if (w->color == red) // case 1 ------> case 2 , case 3 ,case 4 { w->color = black; x->p->color =red; Left_Rotate(&T,x->p); w = x->p->right; } if ( w->left->color == black && w->right->color == black ) //case 2 ------>go on / stop { w->color = red; x = x->p; } else if ( w->right->color == black) // case 3 ---->case 4---->stop { w->left->color = black; w->color =red ; Right_Rotate(&T,w); w = x->p->right ; //转成case 4处理 w->color = x->p->color; x->p->color = black; w->right->color = black; Left_Rotate(&T,x->p); x = T; } else // case 4 ------------------->stop { w->color = x->p->color; x->p->color = black; w->right->color = black; Left_Rotate(&T,x->p); x = T; } } else { w = x->p->left; if (w->color == red) // case 1 ------> case 2 , case 3 ,case 4 { w->color = black; x->p->color =red; Right_Rotate(&T,x->p); w = x->p->left; } if ( w->right->color == black && w->left->color == black ) //case 2 ------>go on/stop { w->color = red; x = x->p; } else if ( w->left->color == black) // case 3 -----> case 4----->stop { w->right->color = black; w->color =red ; Left_Rotate(&T,w); w = x->p->left ; //转成case 4处理 w->color = x->p->color; x->p->color = black; w->left->color = black; Right_Rotate(&T,x->p); x = T; } else // case 4 -------------->stop { w->color = x->p->color; x->p->color = black; w->left->color = black; Right_Rotate(&T,x->p); x = T; } } } x->color = black; //可能由case2退出,那把x涂黑即可,见分析! }
/* CSDN 勿在浮砂筑高台 http://blog.csdn.net/luoshixian099 2015年5月16日 */ #include <STDIO.H> #include <STDLIB.H> enum colors{red,black};//枚举类型 typedef struct Node { struct Node * p; struct Node *left; struct Node *right; int key; enum colors color; }Node; Node *T_NIL=NULL; //建立全部变量 T_NIL Node * Tree_Minimum(Node * T) //找最小结点 { while(T->left != T_NIL) T=T->left; return T; } void Inorder_Tree_Walk(Node * T) //中序遍历树T,输出 { if ( T != T_NIL) { Inorder_Tree_Walk(T->left); //递归其左孩子 printf("%d",T->key); //输出根的关键字 if (T->color == 0) { printf("-R "); //输出结点的颜色 } else { printf("-B "); } Inorder_Tree_Walk(T->right); //递归其右孩子 } } void Pre_Tree_Walk(Node * T) // { if ( T != T_NIL) { printf("%d ",T->key); //输出根的关键字 Pre_Tree_Walk(T->left); //递归其左孩子 Pre_Tree_Walk(T->right); //递归其右孩子 } } void Left_Rotate(Node **T,Node * x) //左旋 { Node *y=x->right; x->right =y->left; if (y->left != T_NIL) y->left->p=x; y->p=x->p; if(x->p==T_NIL) *T=y; else if (x == x->p->left) x->p->left = y; else x->p->right = y; y->left = x; x->p=y; } void Right_Rotate(Node **T,Node * y) //右旋 { Node *x=y->left; y->left =x->right; if (x->right != T_NIL) x->right->p=y; x->p=y->p; if(y->p==T_NIL) *T=x; else if (y == y->p->left) y->p->left = x; else y->p->right = x; x->right = y; y->p=x; } Node* RB_Insert_Fixup(Node *T,Node *z) { Node * y=NULL; while( z->p->color == red) //违反了性质4,迭代进行修正 { if (z->p == z->p->p->left) { y = z->p->p->right; if ( y->color == red) // case 1 叔结点为红色 { z->p->color = black; //父节点涂黑 y->color = black; //叔结点涂黑 z->p->p->color = red; //祖结点涂红 z = z->p->p; //向上迭代,更新z的位置 } else if ( z == z->p->right) //case 2 叔结点为黑色且z为双亲的右孩子 { z = z->p; Left_Rotate(&T,z); z->p->color = black; //case2 已转为 case3 继续处理 z->p->p->color = red; Right_Rotate(&T,z->p->p);// while循环终止 } else // case 3 叔结点为黑色且z为双亲的左孩子 { z->p->color = black; z->p->p->color = red; Right_Rotate(&T,z->p->p);// while循环终止 } } else //对称处理 { y = z->p->p->left; if ( y->color == red) // case 1 叔结点为红色 { z->p->color = black; y->color = black; z->p->p->color = red; z = z->p->p; } else if ( z == z->p->left) //case 2 叔结点为黑色且z为双亲的右孩子 { z = z->p; Right_Rotate(&T,z); z->p->color = black; z->p->p->color = red; Left_Rotate(&T,z->p->p);// } else // case 3 { z->p->color = black; z->p->p->color = red; Left_Rotate(&T,z->p->p); } } } T->color = black; //保证不会违反性质2,对根节点涂黑 return T; } Node *RB_Insert(Node *Root,Node * z) //红黑树插入,返回树的根 { Node * y=T_NIL; Node * x=Root; while( x != T_NIL) //找到结点z要插入的位置 { y=x; if (z->key < x->key) x = x->left; else x = x->right; } z->p = y; if ( y == T_NIL) //插入第一个结点作为根节点的情况 Root = z; else if (z->key < y->key) y->left = z; else y->right = z; Root = RB_Insert_Fixup(Root,z); //插入完毕后,对红黑树的颜色进行修正 return Root; } Node * Establish(int *A,int len) //建立红黑树 { Node * T,*node; int i=0; node=NULL; T_NIL=(Node *)malloc(sizeof(Node)); //建立T_NIL结点 T_NIL->p=NULL; T_NIL->left=NULL; T_NIL->right=NULL; T_NIL->key=-1; T_NIL->color=black; T=T_NIL; for (i=0;i<len;i++) { node =(Node *)malloc(sizeof(Node)); node->p =T_NIL; node->left=T_NIL; node->right=T_NIL; node->key=A[i]; node->color=red; T=RB_Insert(T,node); } return T; } void RB_Transplant(Node **T,Node * u,Node * v) //结点替代函数 { if (u->p == T_NIL) *T = v; else if (u == u->p->left) u->p->left = v; else u->p->right = v; v->p = u->p; //此处赋值无条件,v如果是T_NIL也要进行赋值 } void RB_Delete_Fixup(Node * T,Node * x) { Node *w=NULL; while( x != T && x->color == black) //循环迭代处理 { if ( x == x->p->left ) { w = x->p->right; if (w->color == red) // case 1 ------> case 2 , case 3 ,case 4 { w->color = black; x->p->color =red; Left_Rotate(&T,x->p); w = x->p->right; } if ( w->left->color == black && w->right->color == black ) //case 2 ------>go on / stop { w->color = red; x = x->p; } else if ( w->right->color == black) // case 3 ---->case 4---->stop { w->left->color = black; w->color =red ; Right_Rotate(&T,w); w = x->p->right ; //转成case 4处理 w->color = x->p->color; x->p->color = black; w->right->color = black; Left_Rotate(&T,x->p); x = T; } else // case 4 ------------------->stop { w->color = x->p->color; x->p->color = black; w->right->color = black; Left_Rotate(&T,x->p); x = T; } } else { w = x->p->left; if (w->color == red) // case 1 ------> case 2 , case 3 ,case 4 { w->color = black; x->p->color =red; Right_Rotate(&T,x->p); w = x->p->left; } if ( w->right->color == black && w->left->color == black ) //case 2 ------>go on/stop { w->color = red; x = x->p; } else if ( w->left->color == black) // case 3 -----> case 4----->stop { w->right->color = black; w->color =red ; Left_Rotate(&T,w); w = x->p->left ; //转成case 4处理 w->color = x->p->color; x->p->color = black; w->left->color = black; Right_Rotate(&T,x->p); x = T; } else // case 4 -------------->stop { w->color = x->p->color; x->p->color = black; w->left->color = black; Right_Rotate(&T,x->p); x = T; } } } x->color = black; //可能由case2退出,那把x涂黑即可,见分析!也可能有case4退出,把根节点涂黑 } Node * RB_Delete(Node *T ,Node *z) { Node * x =NULL; Node * y =z; enum colors y_original_color = y->color; //记录下删除前z的颜色 if ( z->left == T_NIL) //左子树不存在的情况 { x = z->right; RB_Transplant(&T,z,z->right); } else if ( z->right == T_NIL) //右子树不存在 { x = z->left; RB_Transplant(&T,z,z->left); } else //左右都存在的情况 { y = Tree_Minimum(z->right); //找到后继y y_original_color = y->color; //记录下y转移前的颜色 x = y->right; if ( y->p == z) //如果y是z的子结点 { x->p = y; } else { RB_Transplant(&T,y,y->right); //如果y不是z的子结点,用y的右子树代替y的位置 y->right = z->right; y->right->p = y; } RB_Transplant(&T,z,y); //y替代z的位置 ,不论y是不是T_NIL y->left = z->left; y->left->p = y; y->color = z->color; //把y的颜色改成z的颜色 } if ( y_original_color == black) //判断y的颜色,若为黑色,需要修复 RB_Delete_Fixup(T,x); return T; } Node * Tree_Search(Node *T ,int k) //寻找数k是否在树中,且返回数k的地址 { while(T !=T_NIL && T->key != k) { if ( k < T->key) T=T->left; else T=T->right; } if ( T == T_NIL) { return NULL; } else { return T; } } void main() { int A[]={2,5,1,6,3,8,4,9,7}; int length = sizeof(A)/sizeof(A[0]); //数组A的长度 Node *T =Establish(A,length); //建立红黑树,返回根节点T printf("中序遍历:\n"); Inorder_Tree_Walk(T);printf("\n"); //中序遍历输出 printf("先序遍历:\n"); //先序遍历输出 Pre_Tree_Walk(T);printf("\n"); printf("-----------删除操作后-------------\n"); T=RB_Delete(T,Tree_Search(T,2)); T=RB_Delete(T,Tree_Search(T,5)); T=RB_Delete(T,Tree_Search(T,7)); T=RB_Delete(T,Tree_Search(T,4)); printf("中序遍历:\n"); Inorder_Tree_Walk(T); printf("\n"); printf("先序遍历:\n"); Pre_Tree_Walk(T); printf("\n"); }
原文地址:http://blog.csdn.net/luoshixian099/article/details/45786665