红黑树RB-tree

本文参考维基百科:http://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91

红黑树是一种自平衡二叉查找树。在每个结点上增加一个存储位表示结点的颜色,可以是RedBlack。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树和AVL树(平衡二叉树)一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用如实时应用中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为建造板块的价值。红黑树是AVL树的一种改进,现基本用的是红黑树

二叉查找树(二叉排序树)的基本性质:

1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

3. 它的左、右子树也分别为二叉排序树。

在一棵二叉查找树上,执行查找、插入、删除等操作,的时间复杂度为Olgn)。因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为Olgn)。但若是一棵具有n个结点的线性链,则此些操作最坏情况运行时间为On)。

而红黑树,能保证在最坏情况下,基本的动态几何操作的时间均为Olgn)。

红黑树的基本性质:

1. 节点是红色或者黑色

2. 根节点为黑色;

3. 所有叶子节点为黑色(叶子节点是NIL节点)

4. 每个红色节点的两个子节点都是黑色节点

5. 从任一节点到其叶子节点的所有简单路径都包含相同数目的黑色节点

左旋就是把节点往下一层移,且为左孩子,很显然只有把右孩子顶替上来,因为右孩子的值比节点大,节点才能成为其左孩子

右旋即把节点往下移一层,成为新的上层节点的右孩子

左旋

红黑树RB-tree_第1张图片

右旋

红黑树RB-tree_第2张图片

红黑树的插入

N表示插入的节点,P表示其父亲节点,G表示其祖父节点,U表示其叔父节点(父亲的兄弟)

插入节点初始为红色

分为5种情况

1. insert_case1

插入的节点为根节点,将其着色为黑色

void insert_case1(node n){

if(n->parent== NULL)

n->color= BLACK;

else

insert_case2(n);

}


 

2. insert_case2

插入节点的父节点为黑色,直接返回即可,并不影响经过该节点的黑色节点数目

void insert_case2(node n){
     if(n->parent->color == BLACK)
         return;/* 树仍旧有效 */
     else
         insert_case3(n);
}


3. insert_case3

父节点和叔父节点都为红色,祖父为红色。

红黑树RB-tree_第3张图片

性质4指出:每个红色节点的两个子节点都是黑色节点。因此将父节点P和叔父节点U着色为黑色,祖父节点着色为红色,通过父节点P或叔父节点U的任何路径都必定通过祖父节点G,在这些路径上的黑节点数目没有改变。但是G的父节点也可能会是红色或者其为根节点,因此需从insert_case1重新来过

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));
     }
     else
         insert_case4(n);
}

1. insert_case4

a) 父节点为红色,叔父节点为黑色,且父节点P是祖父节点G的左孩子,节点N为父节点P的右孩子.P->key <= N->key <= G->key

b) 另一种情况也类似:父节点为红色,叔父节点为黑色,且父节点P是祖父节点G的右孩子,节点N为父节点P的左孩子.G->key <= N->key <= P->key

该两种情形的共同之处父节点与叔父节点颜色不同,且父节点P和节点N不是同为左孩子或同为右孩子,即N的值介于GU之间

解决的办法就是将其转化为情形5即同为左孩子或同为右孩子,即其值要么同大于祖父,要么同小于祖父

红黑树RB-tree_第4张图片

void insert_case4(node n){
     if(n == n->parent->right && n->parent == grandparent(n)->left){
         rotate_left(n->parent);
         n = n->left;
     }elseif(n == n->parent->left && n->parent == grandparent(n)->right){
         rotate_right(n->parent);
         n = n->right;
     }
     insert_case5(n);
}

1. insert_case5

a) 父节点为红色,叔父节点为黑色,且父节点P是祖父节点G的左孩子,节点N也为父节点P的左孩子.N->key <= P->key <= G->key

b) 父节点为红色,叔父节点为黑色,且父节点P是祖父节点G的右孩子,节点N也为父节点P的右孩子.G->key <=P->key <= G->key

红黑树RB-tree_第5张图片

对于a情况,将祖父节点右旋,重新着色(可理解为交换祖父节点和父节点的颜色)。

对于b情况,将祖父节点左旋,重新着色(可理解为交换祖父节点和父节点的颜色)。

对于a情况为什么将祖父节点右旋呢?因为有节点N和父节点都为红色,只能通过祖父节点来修复解决,不可能往更下层修复,举个反例,如果N的两个儿子都是叶子节点。所以只能通过祖父节点来修复,不能简单的将父节点PN着色为黑色,那会破坏性质5:从任一节点到其叶子节点的所有简单路径都包含相同数目的黑色节点,单纯的将PN着为黑色,使通过PS的路径黑色节点数加1,因此将祖父节点右旋,且将父节点P和祖父节点的颜色交换,这样既不破坏性质4也不破坏性质5

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{
         /* Here, n == n->parent->right && n->parent == grandparent(n)->right */
         rotate_left(grandparent(n));
     }
}


红黑树的删除

S表示兄弟节点(原为叔父节点,但当孩子节点替换掉父节点后为兄弟节点)

删除任何一个节点,都可以等效为删除一个至多只有一个儿子(非叶子节点)的节点。删除一个节点,我们可以把左子树中key最大的节点A或右子树中key最小的节点B代替该节点,即复制AB的值到该节点,然后删除AB,所以说等效于删除AB,其必然为至多只有一个儿子为非叶子节点。

如果N的节点是黑色,则删除该节点会是通过N的路径上的黑色节点个数少1,因此需要修复。孩子节点为红色,只需重绘为黑色即可

当孩子节点是黑色,有如下6钟情况需要考虑。后面图中画的N指的是孩子节点N,并非是删除的节点,所给的图是需要我们去修复的红黑树,即删除完节点之后

void
delete_one_child(struct node *n)
{
        /*
         * Precondition: n has at most one non-null child.
         */
        struct node *child = is_leaf(n->right) ? n->left : n->right;
        replace_node(n, child);
        if (n->color == BLACK) {
                if (child->color == RED)
                        child->color = BLACK;
                else
                        delete_case1(child);
        }
        free(n);
}


 

1. Delete_case1

节点N为新根,之前删除的节点使得所有路径黑色节点数少1,因此不影响

void
delete_case1(struct node *n)
{
        if(n->parent != NULL)
                delete_case2(n);
}


2. Delete_case2

叔父节点为红色。

将父节点左旋,交换SP的颜色,将其转为4,5,6,的情况。现在 N 有了一个黑色的兄弟和一个红色的父亲 (它的新兄弟是黑色因为它是红色S的一个儿子),所以我们可以接下去按 456情况来处理。

红黑树RB-tree_第6张图片

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.Delete_case3

父节点P,兄弟节点S为黑色,兄弟节点的两个子节点也为黑色。

显然,只需将S节点颜色变为红色,使得通过S的路径和通过N的路径黑色节点数相同,这样就解决了两个路径黑色节点数不同的问题,但显然通过P节点的路径黑色节点数少1,这就相当于P是先的要修复的失衡节点,从delete_case1开始

红黑树RB-tree_第7张图片

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);
}


1. Delete_case4

父节点为红色,SS的儿子都为黑色,简单的交换,SP的颜色即可解决问题。比将父节点左旋简单的多

红黑树RB-tree_第8张图片

1. Delete_case5

情况5和情况6是对情况3的补充,因为S的儿子节点中至少有一个为红色节点,无法修改S的颜色来解决

a) S 是黑色,S的左儿子是红色,S的右儿子是黑色,S是它父亲的右儿子, N 是它父亲的左儿子(P->key<=SL->key<=S->key。在这种情况下我们在 S 上做右旋转,这样 S 的左儿子成为 S的父亲和 N 的新兄弟。我们接着交换 S和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在 N 有了一个右儿子是红色的黑色兄弟,所以我们进入了情况 6

b) S 是黑色,S的右儿子是红色,S的右儿子是黑色,S是它父亲的左儿子, N 是它父亲的右儿子(S->key<=SR->key<=P->key)。

同样还是把S的红色儿子节点的值介于SP之间的情况,转为同大或同小

同大同小与插入从4转到5类似

红黑树RB-tree_第9张图片

void
delete_case5(struct node *n)
{
        struct node *s = sibling(n);
 
        if  (s->color == BLACK){/* this if statement is trivial, 
due to Case 2 (even though Case two changed the sibling to a sibling's child, 
the sibling's child can't be red, since no red parent can have a red child). */
// the following statements just force the red to be on the left of the left of the parent, 
// or right of the right, so case six will rotate correctly.
                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);
                }elseif((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);
}


1. Delete_case6

a) S是黑色,S的右儿子是红色, S是它父亲的右儿子, N是它父亲的左儿子

b) S是黑色,S的左儿子是红色,S是它父亲的左儿子, N是它父亲的右儿子

红黑树RB-tree_第10张图片

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);
        }
}


 

你可能感兴趣的:(数据结构,struct,null,delete,存储,insert)