红黑树

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

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

红黑树(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





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

性质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 -1

void 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 red
            if(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 node
                if(z == z->p->right)
                {
                    z = z->p;
                    left_rotate(T, z);
                }
                // Case3: z uncle node y is black, but z is left node
                z->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;
        else
            return 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;
    }
    else
        return ERR;

    while(x != T->nil) //y is parent point of x
    {
        y = x;
        if(z->data < x->data)
            x = x->left;
        else
            x = x->right;
    }
    z->p = y;
    if(y == T->nil)
        T->root = z;
    else if(z->data < y->data)
        y->left = z;
    else
        y->right = z;

    z->left = T->nil;
    z->right = T->nil;
    z->color = 'R';

    if( rbtree_fixup(T, z) )
        return OK;
    else
        return 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;
        else
            return 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");
    }
    else
        printf("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树的联系,思路清晰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值