继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

红黑树

九日王朝
关注TA
已关注
手记 180
粉丝 41
获赞 185

很多人认为红黑树很难懂,其实红黑树并没有我们想象中的那么难

首先我们先看红黑树到底是干什么的

红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

我们看到,关于二叉树基本上都是干这样的事,每次排除一个子树,递归另一个子树,使其时间复杂度为O(log n),然后以一种特殊的方法去维护他

比如说  

           50

      25      60

13    30   55   88

这样的树,他的左孩子总是比父节点小,右孩子总是比父节点大,假设我们搜索55,其实就相当于我们先询问

节点1(50)   发现55比他大,然后去其右孩子问

节点3(60)   发现55比他小,然后去其左孩子问

节点6(55)   找到了~


就是这样。但如果我现在要插入一些数怎么办呢?比如说我要加一个31,很显然我应该放在30的右孩子

那么就变成了


           50

      25      60

13    30   55   88

           31

我要再来一个32呢,还得往下,再来一个33呢? 所以我们看出,如果我不改变整个树的结构,那么我很难构造出真正实现O(log n)的二叉树

因为随着插入、删除等操作,其父节点不会再是一组数列的中心点了。那么到底要怎么改变树的结构,成为了不同查找树算法的区分。


好了,现在我们大概明白它要干什么事了,然后我们再看看红黑树为什么要分红和黑

这里我们要引用一下2-3树的知识。

2-3树是最简单的B-树(或-树)结构,其每个非叶节点都有两个或三个子女,而且所有叶都在统一层上。2-3树不是二叉树,其节点可拥有3个孩子。不过,2-3树与满二叉树相似。高为h的2-3树包含的节点数大于等于高度为h的满二叉树的节点数,即至少有2^h-1个节点。

就是说,为了保证查找树的平衡性,我们需要一些灵活性,因此在这里我们允许树中的一个结点保存多个键。
2-结点:含有一个键(及值)和两条链接,左链接指向的2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。
3-结点:含有两个键(及值)和三条链接,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。

看了上篇博客线段树的同学可能会明白一些       http://blog.csdn.net/sm9sun/article/details/53302873

说白了,就是2-3树,他的值可以是某个点,也可以是一个线段。也就是说:

2叉:

           【a】

【小于a】【大于a】


3叉:

                 【a,b】

【小于a】【ab之间】【大于b】


好了,那么红黑树和2-3树有什么联系呢??

红黑树的本质:
红黑树是对2-3查找树的改进,它能用一种统一的方式完成所有变换。

替换3-结点
红黑树背后的思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换3-结点)来表示2-3树。
我们将树中的链接分为两种类型:红链接将两个2-结点连接起来构成一个3-结点,黑链接则是2-3树中的普通链接。确切地说,我们将3-结点表示为由一条左斜的红色链接相连的两个2-结点。这种表示法的一个优点是,我们无需修改就可以直接使用标准二叉查找树的get()方法。对于任意的2-3树,只要对结点进行转换,我们都可以立即派生出一颗对应的二叉查找树。我们将用这种方式表示2-3树的二叉查找树称为红黑树。



*以下内容转载于http://blog.csdn.net/yang_yulei/article/details/26066409



https://img2.mukewang.com/5b504b9100016fbb03800459.jpg


所以我们看一下红黑树的性质到底在说些什么:

性质1. 节点是红色或黑色。(2-3树变型)
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)(一个节点不可能与两条红链接相连)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。(黑链接(2叉)平衡)


接下来我们说一下为什么左旋、右旋,其实特别简单,就如上文说的一样,

父节点无法保证自己处于平衡状态,那么就必须通过旋转来保证树的平衡性

比如:


           50

      25      60

13    30   

当我插入一个10时,左边的深度比右边深2层,那么就必须需要旋转

那么就变成了


           25

      13     50

  10      30    60


所以具体的着色、旋转操作要看我们插入删除不同的情况而定

*以下内容转载于http://blog.csdn.net/v_JULY_v/article/details/6105630 

红黑树插入的几种情况:
情况1,z的叔叔y是红色的。
情况2:z的叔叔y是黑色的,且z是右孩子
情况3:z的叔叔y是黑色的,且z是左孩子
红黑树删除的几种情况。
情况1:x的兄弟w是红色的。
情况2:x的兄弟w是黑色的,且w的俩个孩子都是黑色的。
情况3:x的兄弟w是黑色的,且w的左孩子是红色,w的右孩子是黑色。
情况4:x的兄弟w是黑色的,且w的右孩子是红色的。


设:要插入的节点为N,父亲节点P,祖父节点G,叔叔节点U,兄弟节点S


如下图所示,找一个节点的祖父和叔叔节点:
node grandparent(node n)     //祖父
{
     return n->parent->parent;
 }
 node uncle(node n)              //叔叔
{
     if (n->parent == grandparent(n)->left)
         return grandparent(n)->right;
     else
         return grandparent(n)->left;
 }



红黑树插入的几种情况
情形1: 新节点N位于树的根上,没有父节点
void insert_case1(node n) {
     if (n->parent == NULL)
         n->color = BLACK;
     else
         insert_case2(n);
 }


情形2: 新节点的父节点P是黑色
void insert_case2(node n) {
     if (n->parent->color == BLACK)
         return; /* 树仍旧有效 */
     else
         insert_case3(n);
 }
情形3:父节点P、叔叔节点U,都为红色,
void insert_case3(node n) {
     if (uncle(n) != NULL && uncle(n)->color == RED) {
         n->parent->color = BLACK;
         uncle(n)->color = BLACK;
         grandparent(n)->color = RED;
         insert_case1(grandparent(n));   //因为祖父节点可能是红色的,违反性质4,递归情形1.
     }
     else
         insert_case4(n);   //否则,叔叔是黑色的,转到下述情形4处理。
情形4: 父节点P是红色,叔叔节点U是黑色或NIL; 
插入节点N是其父节点P的右孩子,而父节点P又是其父节点的左孩子。
void insert_case4(node n) {
     if (n == n->parent->right && n->parent == grandparent(n)->left) {
         rotate_left(n->parent);
         n = n->left;
     } else if (n == n->parent->left && n->parent == grandparent(n)->right) {
         rotate_right(n->parent);
         n = n->right;
     }
     insert_case5(n);    //转到下述情形5处理。
情形5: 父节点P是红色,而叔父节点U 是黑色或NIL,
要插入的节点N 是其父节点的左孩子,而父节点P又是其父G的左孩子。
void insert_case5(node n) {
     n->parent->color = BLACK;
     grandparent(n)->color = RED;
     if (n == n->parent->left && n->parent == grandparent(n)->left) {
         rotate_right(grandparent(n));
     } else {
         /* 反情况,N 是其父节点的右孩子,而父节点P又是其父G的右孩子 */
         rotate_left(grandparent(n));
     }
 }


红黑树删除的几种情况
上文我们约定,兄弟节点设为S,我们使用下述函数找到兄弟节点:
struct node * sibling(struct node *n)  //找兄弟节点
{
        if (n == n->parent->left)
                return n->parent->right;
        else
                return n->parent->left;
}

情况1: N 是新的根。
void
delete_case1(struct node *n)
{
        if (n->parent != NULL)
                delete_case2(n);
}
情形2:兄弟节点S是红色
void delete_case2(struct node *n)
{
        struct node *s = sibling(n);
 
        if (s->color == RED) {
                n->parent->color = RED;
                s->color = BLACK;
                if (n == n->parent->left)
                        rotate_left(n->parent);  //左旋
                else
                        rotate_right(n->parent);
        } 
        delete_case3(n);
}


情况 3: 兄弟节点S是黑色的,且S的俩个儿子都是黑色的。但N的父节点P,是黑色。
void delete_case3(struct node *n)
{
        struct node *s = sibling(n);
 
        if ((n->parent->color == BLACK) &&
            (s->color == BLACK) &&
            (s->left->color == BLACK) &&
            (s->right->color == BLACK)) {
                s->color = RED;
                delete_case1(n->parent);
        } else
                delete_case4(n);
}


情况4: 兄弟节点S 是黑色的、S 的儿子也都是黑色的,但是 N 的父亲P,是红色。
void delete_case4(struct node *n)
{
        struct node *s = sibling(n);
 
        if ((n->parent->color == RED) &&
            (s->color == BLACK) &&
            (s->left->color == BLACK) &&
            (s->right->color == BLACK)) {
                s->color = RED;
                n->parent->color = BLACK;
        } else
                delete_case5(n);
}
情况5: 兄弟S为黑色,S 的左儿子是红色,S 的右儿子是黑色,而N是它父亲的左儿子。
//此种情况,最后转化到下面的情况6。
void delete_case5(struct node *n)
{
        struct node *s = sibling(n);
 
        if  (s->color == BLACK) 
                if ((n == n->parent->left) &&
                    (s->right->color == BLACK) &&
                    (s->left->color == RED)) { 
                        // this last test is trivial too due to cases 2-4.
                        s->color = RED;
                        s->left->color = BLACK;
                        rotate_right(s);
                } else if ((n == n->parent->right) &&
                           (s->left->color == BLACK) &&
                           (s->right->color == RED)) {
                       // this last test is trivial too due to cases 2-4.
                        s->color = RED;
                        s->right->color = BLACK;
                        rotate_left(s);
                }
        }
        delete_case6(n);  //转到情况6


情况6: 兄弟节点S是黑色,S的右儿子是红色,而 N 是它父亲的左儿子。
[对应我第二篇文章中,情况4:x的兄弟w是黑色的,且w的右孩子时红色的。]
void delete_case6(struct node *n)
{
        struct node *s = sibling(n);
 
        s->color = n->parent->color;
        n->parent->color = BLACK;
 
        if (n == n->parent->left) {
                s->right->color = BLACK;
                rotate_left(n->parent);
        } else {
                s->left->color = BLACK;
                rotate_right(n->parent);
        }
}

红黑树完整代码:


#include <stdio.h>#include <stdlib.h>#include <string.h>typedef int DataTypedef;typedef struct RBTREENODE{	int data;	char color;	struct RBTREENODE *left;	struct RBTREENODE *right;	struct RBTREENODE *p;}rbtreeNode;typedef struct RBTREE{	rbtreeNode *root;	rbtreeNode *nil;}rbTree;#define OK 1#define ERR -1void rbtree_free_node(rbTree *T, rbtreeNode *p){if(p != T->nil){rbtree_free_node(T, p->left);rbtree_free_node(T, p->right);free(p);}}void rbtree_show_node(rbTree *T, rbtreeNode *p){if(p != T->nil){printf("%02d ", p->data);p->color == 'R' ? printf(" R\n") : printf(" B\n");rbtree_show_node(T, p->left);rbtree_show_node(T, p->right);}}void left_rotate(rbTree *T, rbtreeNode * x){	rbtreeNode *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->root = 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(rbTree *T, rbtreeNode *x){	rbtreeNode * y = x->left;	x->left = y->right;	if(y->right != T->nil)		y->right->p = x;	y->p = x->p;	if(x->p == T->nil)		T->root = y;	else if(x == x->p->left)		x->p->left = y;	else		x->p->right = y;	y->right = x;	x->p = y;}int rbtree_fixup(rbTree *T, rbtreeNode *z){rbtreeNode *y = NULL;/*    * while循环在每次迭代的开头都保持3个部分的不变式    * 1 节点z是红色节点    * 2 如果z.p是根节点,则z.p是黑节点    * 3 如果有任何红黑性质被破坏,则至多只有一条被破坏,或是性质2(根为黑色节点),或是性质4(如果一个节点是红色的,    *   则它的两个子节点都是黑色的)    */while(z->p->color == 'R'){if(z->p == z->p->p->left)//在所有的情况中,z的祖父节点z.p.p是黑色的,以为z.p是红色的{y = z->p->p->right;// Case1: z uncle node y is redif(y->color == 'R'){z->p->color = 'B';y->color = 'B';z->p->p->color = 'R';z = z->p->p;}else{// Case2: z uncle node y is black, but z is right nodeif(z == z->p->right){z = z->p;left_rotate(T, z);}// Case3: z uncle node y is black, but z is left nodez->p->color = 'B';z->p->p->color = 'R';right_rotate(T, z->p->p);}}else{y = z->p->p->left;if(y->color == 'R'){z->p->color = 'R';y->color = 'B';z->p->p->color = 'R';z = z->p->p;}else{if(z == z->p->left){z = z->p;right_rotate(T, z);}z->p->color = 'B';z->p->p->color = 'R';left_rotate(T, z->p->p);}}}T->root->color = 'B';return OK;}/** blow funtion is used in other file*/rbTree * rbtree_init(){	rbTree *T = NULL;	T = (rbTree *)malloc(sizeof(rbTree));	if(T == NULL)	{		printf("T no space\n");	}			T->nil = (rbtreeNode *)malloc(sizeof(rbtreeNode));	if(T->nil == NULL)	{		printf("T->nil no space\n");	}			T->root = T->nil;	T->nil->data = (1<<31);	T->nil->color = 'B';	T->nil->left = T->nil;	T->nil->right = T->nil;	T->nil->p = T->nil;	return T;}int rbtree_in(rbTree *T, DataTypedef x){rbtreeNode *p = T->root;while(p != T->nil){if(x < p->data)p = p->left;else if(x > p->data)p = p->right;elsereturn OK;}return ERR;}int rbtree_insert(rbTree *T, DataTypedef num){rbtreeNode *y = T->nil;rbtreeNode *x = T->root;rbtreeNode *z = NULL;if(!rbtree_in(T, num)){z = (rbtreeNode *)malloc(sizeof(rbtreeNode));if(z == NULL){        	printf("no space for z in rbtree\n");		}z->data = num;}elsereturn ERR;while(x != T->nil) //y is parent point of x{y = x;if(z->data < x->data)x = x->left;elsex = x->right;}z->p = y;if(y == T->nil)T->root = z;else if(z->data < y->data)y->left = z;elsey->right = z;z->left = T->nil;z->right = T->nil;z->color = 'R';if( rbtree_fixup(T, z) )return OK;elsereturn ERR;}rbtreeNode *rbtree_find(rbTree *T, DataTypedef x){rbtreeNode *p = T->root;while(p != T->nil){if(x < p->data)p = p->left;else if(x > p->data)p = p->right;elsereturn p;}return T->nil;}rbtreeNode *rbtree_findmin(rbTree *T, rbtreeNode *p){rbtreeNode *q = p;while(p != T->nil){q = p;p = p->left;}return q;}void rbtree_transplant(rbTree *T, rbtreeNode *u, rbtreeNode *v){if(u->p == T->nil){T->root = v;}else if(u == u->p->left){u->p->left = v;}else{u->p->right = v;}v->p = u->p;}void rbtree_delete_fixup(rbTree *T, rbtreeNode *x){rbtreeNode *w;while(x != T->root && x->color == 'B')//while循环中,x总是指向一个具有双重黑色的非根界节点{if(x == x->p->left){w = x->p->right;if(w->color == 'R') //情况1:x的兄弟节点w是红色{w->color = 'B';x->p->color = 'R';left_rotate(T, x->p);w = x->p->right;}if(w->left->color == 'B' && w->right->color == 'B') //情况2:x的兄弟节点w是黑色,而且w的两个子节点都是黑色的{w->color = 'R';x = x->p;}else{if(w->right->color == 'B') //情况3:x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的{w->left->color = 'B';w->color = 'R';right_rotate(T, w);w = x->p->right;}w->color = x->p->color;     //情况4:x的兄弟节点w是黑色的,且w的右孩子是红色的x->p->color = 'B';w->right->color = 'B';left_rotate(T, x->p);x = T->root;}}else//x = x->p->right{w = x->p->left;if(w->color == 'R'){w->color = 'B';x->p->color = 'R';right_rotate(T, x->p);w = x->p->left;}if(w->left->color == 'B' && w->right->color == 'B'){w->color = 'R';x = x->p;}else{if(w->left->color == 'B'){w->right->color = 'B';w->color = 'R';left_rotate(T, w);w = x->p->left;}w->color = x->p->color;x->p->color = 'B';w->left->color = 'B';right_rotate(T, x->p);x = T->root;}}}x->color = 'B';}void rbtree_delete(rbTree *T, DataTypedef data){rbtreeNode *z = rbtree_find(T, data);rbtreeNode *y;rbtreeNode *x;char y_oldcolor;if(z == T->nil){printf("the rbtree hasn't %d\n", data);return;}y = z;y_oldcolor = y->color;if(z->left == T->nil){x = z->right;rbtree_transplant(T, z, z->right);}else if(z->right == T->nil){x = z->left;rbtree_transplant(T, z, z->left);}else{y = rbtree_findmin(T, z->right);y_oldcolor = y->color;x = y->right;if(y->p == z){x->p = y;}else{rbtree_transplant(T, y, y->right);y->right = z->right;y->right->p = y;}rbtree_transplant(T, z, y);y->left = z->left;y->left->p = y;y->color = z->color;}if(y_oldcolor == 'B')rbtree_delete_fixup(T, x);free(z);//printf("free the node is ok\n\n");}void rbtree_show(rbTree *T){if(T->root != T->nil){rbtree_show_node(T, T->root);printf("\n");}elseprintf("This rbtree is empty.\n");}int rbtree_free(rbTree *T){if(T->root != T->nil){rbtree_free_node(T, T->root);}free(T->nil);free(T);return OK;}int main(void){rbTree *T = NULL;T = rbtree_init();int n,value;while(~scanf("%d",&n)&&n)		{			switch(n)			{				case 1:scanf("%d",&value);rbtree_insert(T, value);break;				case 2:rbtree_show(T);break;				case 3:scanf("%d",&value); rbtree_delete(T, value);break;				default:break;			}}rbtree_free(T);return 0;}




最后,推荐本人认为两个比较不错的博客,强烈建议大家去看一下

http://blog.csdn.net/v_JULY_v/article/details/6105630         含多篇关于红黑树的讲解,内容详细。

http://blog.csdn.net/yang_yulei/article/details/26066409        直接说明红黑树与2-3树的联系,思路清晰。


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP