红黑树的使用详解

2016-02-19 10:06 7 1 收藏

下面是个红黑树的使用详解教程,撑握了其技术要点,学起来就简单多了。赶紧跟着图老师小编一起来看看吧!

【 tulaoshi.com - 编程语言 】

(学习的参考资料主要是《算法导论》)

  首先是红黑树的性质。一棵二叉查找树满足以下的红黑性质,则为一棵红黑树。

  1)每个结点或是红的,或是黑的。

  2)根结点是黑的。

  3)每个叶结点(NIL)是黑的。

  4)红结点的两个孩子都是黑的。

  5)对任意结点,从它到其子孙结点所有路径上包含相同数目的黑结点。

  初学时并不在意,但是仔细研究相关算法就会知道,算法都是围绕保持这些性质来进行的。性质5)保证了红黑树使用时的高效。定理证明了n个内结点的红黑树高度至多为2lg(n+1)。

  不同于一般二叉查找树,红黑树一般采用一个哨兵结点代表NIL,这对算法的使用提供了很多方便,具体编写时可以体会的到。哨兵设置为黑色,它是根的父结点,也是所有的叶子结点。而它的其他域可以设置为任意值。我用关键字把它和普通的结点进行区分。

  旋转是红黑树的特有操作。以前搞不清左旋和右旋究竟是如何进行的,现在比较明白,可以这样概括:以x结点左旋即为,使x从一棵子树的根变成这个子树的左孩子;对称的,同理。旋转是红黑树插入和删除时为了维持红黑性质而可能进行的操作。

  插入的原理:

  除了空指针的处理,插入的过程和二叉查找树相同,但是插入后需要进行独有的调整算法以保证红黑性质。下面的描述是我的个人概括,看上去比较混乱,和算法以及实例相对照着可能容易理解一些。

  新插入的点z直接染成红色,再根据其父结点是否为红(与性质4冲突)和插入的结点是否为根(与性质2冲突)进行调整。后者直接把根染黑即可。

  对于前者,找到z的叔叔y(找叔叔y虽然需要分情况处理,但比较简单,不详写),根据y是红还是黑进一步分清况。z的父亲为左孩子时,前者只需要把z的父亲和叔叔同时变黑、z的父结点的父结点变红、令z指向z的父结点的父结点迭代处理即可;后者进一步分z是左孩子还是右孩子处理。z是左孩子时直接以z的父结点进行旋转让z的父亲左旋并成为新z即成为后一种情况。在后一种情况中,将z的父亲染黑,祖父染红,以z的祖父右旋就能获得。

  删除的原理:

  算法导论上的删除算法把两种情况同时进行处理,确实很有技巧。红黑树的删除除了最后需要根据对于删除结点的颜色来判断是否需要进行调整外,和普通的二叉查找树没有区别,这里稍微做一下分析。
代码如下:

RB-DELETE(T, z)                     //   情况1           ||  情况2
 if left[z] = nil[T] or right[z] = nil[T]    //  z最多一个孩子时       ||  z有两个孩子时
   then y ← z                    //   令y=z           ||   令y是z后继(此时y必然不是z的右孩子)
   else y ← TREE-SUCCESSOR(z)           //===============================================================================
 if left[y] ≠ nil[T]                //  令x为y的孩子或哨兵      ||   令x是y的右孩子(x必然不为左孩子,否则y不可能是z的后继)
    then x ← left[y]                //                          ||    将来x会代替y的位置
    else x ← right[y]               //================================================================================
 p[x] ← p[y]                    //
 if p[y] = nil[T]                 //
     then root[T] ← x               //                         x与x的新父亲之间建立关系
     else if y = left[p[y]]           //
           then left[p[y]] ← x          //
           else right[p[y]] ← x         //=================================================================================
 if y !≠ z                     //                  ||
     then key[z] ← key[y]                     //       删完后整体上移       ||    替代,用于替代的原结点删除
         copy y's satellite data into z       //                             ||
 if color[y] = BLACK                          //                             ||
     then RB-DELETE-FIXUP(T, x)               //                             ||
  return y

   删除后,如果删除的结点是黑色,可能会造成性质2、4、5的违反。调整算法思想是使得代替y的x多染一层黑色而成为红黑或二重黑色结点。这个处理只是用指针x标示,并不用改变结点color域的内容。调整算法按8种情况,其中两两对称,只描述4种。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  用w表示x的兄弟。

  情况1为w红。此时调整w为黑,p[x]为红,以p[x]左旋,w指向x的新兄弟,此时则成为情况2或3或4。

  情况2为w黑,且w的两个孩子均黑。此时把w染红,令p[x]成为新的x。这相当于把x剥离了一层黑色,使这层黑色上移。

  情况3为w黑,w的左孩子为红,右孩子为黑。这时交换w和左孩子颜色,并对w右旋,此时成为情况4。

  情况4为w黑,w有孩子为红。这时使w成为p[x]的颜色,p[x]置为黑色,w的右孩子置为黑,对p[x]左旋。令x为根。这时相当于把原先x上的一重黑色传给了其父亲并于它一起下移,而w代替了其父亲原先的颜色和位置。这是不存在红黑结点或二重黑结点。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  每次处理都判断x是否为根且x是否为黑。x不为根且为黑时才代表有红黑结点或二重黑结点,需要进行新一轮循环。循环结束后把根染黑就结束了。

  最后附上一个我自己用C写的红黑树操作。插入操作验证无误,删除操作验证次数有限,可能有bug存在。

代码如下:

#include    stdlib.h
#include    stdio.h

#define T_nil -1
//T_nil is a key of nil[T] in the book.
#define RED        1
#define BLACK    0//T_nil is BLACK

//T_nil's p is itself. need to set.

struct rb_tree {
    int color;
    int key; //normally a positive number.
    struct rb_tree *left;
    struct rb_tree *right;
    struct rb_tree *p;
};

int rb_walk(struct rb_tree *node) {
    if (node-key != T_nil) {
        rb_walk(node-left);
        printf("%d, color is %sn",node-key,(node-color?"RED":"BLACK"));
        rb_walk(node-right);
    }
    return 1;
}

struct rb_tree* rb_search(struct rb_tree *node, int k) {
    if ((node-key == T_nil) || (node-key == k))
        return node;

    if ( k node-key )
        return rb_search(node-left,k);
    else
        return rb_search(node-right,k);
}

struct rb_tree* tree_minimum(struct rb_tree *node) {
    if (node-key == T_nil)
        return node;
    while (node-left-key != T_nil)
        node = node-left;
    return node;
}

struct rb_tree* tree_maximum(struct rb_tree *node) {
    if (node-key == T_nil)
        return node;
    while (node-right-key != T_nil)
        node = node-right;
    return node;
}

struct rb_tree* tree_successor(struct rb_tree *node) {
    struct rb_tree *y;
    if (node-right-key != T_nil)
        return tree_minimum(node-right);
    y = node-p;
    while ((y-key != T_nil) && (node == y-right)) {
        node = y;
        y = y-p;
    }
    return y;
}
//3 function of below is copy from tree.

struct rb_tree * left_rotate(struct rb_tree *rb, struct rb_tree *x) {
    struct rb_tree *y;
    //if (x-right-key == T_nil) {
    //    printf("have no right child,rotation cancel.n");
    //    return rb;
    //}
    y = x-right;
    x-right = y-left;
    if (y-left-key != T_nil)
        y-left-p = x;
    y-p = x-p;
    if    (x-p-key == T_nil)
        rb = y;
    else if (x == x-p-left)
        x-p-left = y;
    else
        x-p-right = y;
    y-left = x;
    x-p = y;
    return rb;
}

struct rb_tree *right_rotate(struct rb_tree *rb, struct rb_tree *x) {
    struct rb_tree *y;
    //if (x-left-key == T_nil ) {
    //    printf("have no left child,rotation cancel.n");
    //    return rb;
    //}
    y = x-left;
    x-left = y-right;
    if (y-right-key != T_nil )
        y-right-p = x;
    y-p = x-p;
    if (x-p-key == T_nil)
        rb = y;
    else if (x == x-p-left)
        x-p-left = y;
    else
        x-p-right = y;
    y-right = x;
    x-p = y;
    return rb;
}

struct rb_tree* rb_insert_fixup(struct rb_tree *rb, struct rb_tree *z) {
    struct rb_tree *y;
    while (z-p-color == RED) {
        if (z-p == z-p-p-left) {
            y = z-p-p-right;
            if (y-color == RED)    {
                z-p-color = BLACK;
                y-color = BLACK;
                z-p-p-color = RED;
                z = z-p-p;
            }
            else {
                if (z == z-p-right) {
                    z= z-p;
                    rb = left_rotate(rb,z);
                }
                z-p-color = BLACK;
                z-p-p-color = RED;
                rb = right_rotate(rb,z-p-p);
            }
        }
        else {//same as right and left exchanged
            y = z-p-p-left;
            if (y-color == RED)    {
                z-p-color = BLACK;
                y-color = BLACK;
                z-p-p-color = RED;
                z = z-p-p;
            }
            else {
                if (z == z-p-left) {
                    z= z-p;
                    rb = right_rotate(rb,z);
                }
                z-p-color = BLACK;
                z-p-p-color = RED;
                rb = left_rotate(rb,z-p-p);
            }   
        }
    }
    rb-color = BLACK;
    return rb;
}

struct rb_tree* rb_insert(struct rb_tree *rb, int k) {
    struct rb_tree *y = rb-p;
    struct rb_tree *x = rb;
    struct rb_tree *z;
    z= (struct rb_tree *)malloc(sizeof(struct rb_tree));
    z-key = k;
    while (x-key != T_nil) {
        y = x;
        if (k x-key)
            x = x-left;
        else
            x = x-right;
    }
    z-p = y;
    if (y-key == T_nil)
        rb = z;
    else if (z-key y-key)
        y-left = z;
    else
        y-right =z;
    z-left = rb-p;
    z-right = rb-p;
    z-color = RED;
    return rb_insert_fixup(rb,z);
}

struct rb_tree* rb_delete_fixup(struct rb_tree *rb, struct rb_tree *x) {
    struct rb_tree *w;
    while ((x !=rb)&&(x-color == BLACK)) {
        if (x == x-p-left) {
            w = x-p-right;
            if (w-color == RED) {
                w-color = BLACK;
                x-p-color = RED;
                left_rotate(rb,x-p);
                w = x-p-right;
            }
            if ((w-left-color == BLACK)&&(w-right-color == BLACK)) {
                w-color = RED;
                x = x-p;
            }
            else if (w-right-color == BLACK) {
                w-left-color = BLACK;
                w-color = RED;
                right_rotate(rb,w);
                w = x-p-right;
            }
            w-color = x-p-color;
            x-p-color = BLACK;
            w-right-color = BLACK;
            left_rotate(rb,x-p);
            x = rb;
        }
        else { //same as right and left exchanged
            w = x-p-left;
            if (w-color == RED) {
                w-color = BLACK;
                x-p-color = RED;
                right_rotate(rb,x-p);
                w = x-p-right;
            }
            if ((w-right-color == BLACK)&&(w-left-color == BLACK)) {
                w-color = RED;
                x = x-p;
            }
            else if (w-left-color == BLACK) {
                w-right-color = BLACK;
                w-color = RED;
                left_rotate(rb,w);
                w = x-p-left;
            }
            w-color = x-p-color;
            x-p-color = BLACK;
            w-left-color = BLACK;
            right_rotate(rb,x-p);
            x = rb;
        }
    }
    x-color = BLACK;
}

struct rb_tree* rb_delete(struct rb_tree *rb, struct rb_tree *z) {
    struct rb_tree *x,*y;
    if ((z-left-key == T_nil) || (z-right-key == T_nil))
        y = z;
    else y = tree_successor(z);
    if (y-left-key != T_nil)
        x = y-left;
    else
        x = y-right;

    x-p = y-p;

    if (y-p-key == T_nil)
        rb = x;
    else if (y==x-p-left)
        y-p-left = x;
    else
        y-p-right = x;

    if (y!=x) //copy y's data to z
        z-key = y-key;
    if (y-color == BLACK)
        rb_delete_fixup(rb,x);
    free(y);
    return rb;
}

int main () {
    struct rb_tree *p,*root;
    struct rb_tree tree_nil = {BLACK,T_nil,&tree_nil,&tree_nil,&tree_nil};
    root = &tree_nil;
    root = rb_insert(root,15);
    root = rb_insert(root,5);
    root = rb_insert(root,16);
    root = rb_insert(root,3);
    root = rb_insert(root,12);
    root = rb_insert(root,20);
    root = rb_insert(root,10);
    root = rb_insert(root,13);
    root = rb_insert(root,6);
    root = rb_insert(root,7);
    root = rb_insert(root,18);
    root = rb_insert(root,23);
    rb_walk(root);
    p = rb_search(root,18);
    root = rb_delete(root,p);
    rb_walk(root);
    return 1;
}

来源:http://www.tulaoshi.com/n/20160219/1593386.html

延伸阅读
const使用详解 作者:康建东 关于C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,现将本人的一些体会总结如下,期望对大家有所帮助: 一 const基础 如果const关键字不涉及到指针,我们很好理解,下面是涉及到指针的情况: int b = 500;const...
标签: 电脑入门
超拽有才的网名 我愿让败类去死。 不冷不热无情无感//* 成绩总挑拨我和电脑的关系 - 移动淫领三妓 有吃有喝有人爱** 扑通扑通 腾讯唯一不让我恨得就是Q 踩着高跷看矮子 我是奇葩淫淫荡荡光芒万丈 让我一次艾格豆 鸟枪袭婊秀贞操 三儿,你真是太贱# 我木胸可我很凶ق 哎呦喂!祝你夜夜高潮精尽人亡. 奈何桥上唱征服// 切糕...
拥有一个最佳的睡眠是每一个人都渴望的,特别是一些长期失眠和后天睡眠质量不好的人,对此更属于望尘莫及的事。影响我们睡眠质量的不光是我们的精神因素,还包括一些生活中的常见食物所致。如果摄入破坏睡眠的这些食物,那么你今晚的睡眠就遭大秧了。 1.芹菜 睡前90分钟别吃。塔夫茨大学的营养学家海伦·拉斯穆森博士...
房屋装修情况: 【装修户主】:浅0910 【装修风格】:简约美式 【装修户型】:一室一厅 【装修面积】:30平 【擅长此风格的装修公司】:千然装饰 【业主自述】:在论坛里当了很久的游客,特别喜欢看别人晒新家,不同的风格,不一样的生活气息,每一处设计上的别出心裁都是对平淡生活的精彩点缀。今天我也要把自己的小家展示出来给大家分...
标签: 祛痘 祛斑 去角质
先认清 你的痘印是哪种? 造成痘痘的原因很多,遗留下的痘印也各有不同。颜色不同,要想知道怎样消除痘印,首先一定要好好辨别一下自己痘印的类型哦。千万别张冠李戴,搞错了类型,应对的方法就会产生错误哦。 类型1:红色痘印 形成原因: 因为原本长痘痘处,细胞发炎引起血管扩张。但是痘痘...

经验教程

183

收藏

95
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部