红黑树的使用详解

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

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

  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种。

  用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代替了其父亲原先的颜色和位置。这是不存在红黑结点或二重黑结点。

  每次处理都判断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 %s\n",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;
}

(0)

相关推荐

  • 详解Linux内核中的container_of函数

    前言 在linux 内核中,container_of 函数使用非常广,例如 linux内核链表 list_head.工作队列work_struct中. 在linux内核中大名鼎鼎的宏container_of() ,其实它的语法很简单,只是一些指针的灵活应用,它分两步: 第一步,首先定义一个临时的数据类型(通过typeof( ((type *)0)->member )获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值. 第二步,用(char *)__mptr减去member在结构体

  • Linux内核模块和驱动的编写

    Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难.为了解决这个问题引入了内核机制.从而可以动态的想内核中添加或者删除模块. 模块不被编译在内核中,因而控制了内核的大小.然而模块一旦被插入内核,他就和内核其他部分一样.这样一来就会曾家一部分系统开销.同时,如果模块出现问题,也许会带来系统的崩溃. 模块的实现机制: 启动时,由函数 void inti_modules() 来初始化模块,因为启动事很多时候没有模块.这个函数往往把内核自身当作一个虚模块. 如由系统需要

  • C语言实现红黑树的实例代码

    因为看内核的时候感觉红黑树挺有意思的,所以利用周末的时间来实现一下玩玩.红黑树的操作主要是插入和删除,而删除的时候需要考虑的情况更多一些.具体的操作就不在这里罗嗦了,百度文库里面有一个比较有好的文章,已经说的很明白了. 在看具体的操作的时候有的人可能感觉有些情况是没有考虑到的(如果没有这种感觉的人很有可能根本没有仔细地想).但是那些"遗漏"的情况如果存在的话,操作之前的红黑树将违反那几个规则. 写代码的时候很多次因为少考虑情况而导致错误,细节比较多,刚开始rb_node中没有指向父节点

  • Linux内核漏洞浅析

    与Windows相比,Linux被认为具有更好的安全性和其他扩展性能.这些特性使得Linux在操作系统领域异军突起,得到越来越多的重视.随着Linux应用量的增加,其安全性也逐渐受到了公众甚或黑客的关注.那么,Linux是否真的如其支持厂商们所宣称的那样安全呢?本期我们请到了启明星辰信息技术有限公司积极防御实验室工程师赵伟,对Linux进行专业的漏洞技术分析. Linux内核精短.稳定性高.可扩展性好.硬件需求低.免费.网络功能丰富.适用于多种cpu等特性,使之在操作系统领域异军突起.其独特的魅

  • Linux操作系统内核编译详解

    内核简介 内核,是一个操作系统的核心.它负责管理系统的进程.内存.设备驱动程序.文件和网络系统,决定着系统的性能和稳定性. Linux的一个重要的特点就是其源代码的公开性,所有的内核源程序都可以在/usr/src/linux下找到,大部分应用软件也都是遵循GPL而设计的,你都可以获取相应的源程序代码.全世界任何一个软件工程师都可以将自己认为优秀的代码加入到其中,由此引发的一个明显的好处就是Linux修补漏洞的快速以及对最新软件技术的利用.而Linux的内核则是这些特点的最直接的代表. 

  • 图解红黑树及Java进行红黑二叉树遍历的方法

    红黑树 红黑树是一种数据结构与算法课堂上常常提到但又不会细讲的树,也是技术面试中经常被问到的树,然而无论是书上还是网上的资料,通常都比较刻板难以理解,能不能一种比较直观的方式来理解红黑树呢?本文将以图形的方式来解释红黑树的插入与删除操作. 对树结构的学习是一个递进的过程,我们通常所接触的树都是二叉树,二叉树简单来说就是每个非叶子节点都有且只有两个孩子,分别叫做左孩子和右孩子.二叉树中有一类特殊的树叫二叉查找树,二叉查找树是一种有序的树,对于每个非叶子节点,其左子树的值都小于它,其右子树的值都大于

  • Linux内核中红黑树算法的实现详解

    一.简介 平衡二叉树(BalancedBinary Tree或Height-Balanced Tree) 又称AVL树.它或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1.若将二叉树上结点的平衡因子BF(BalanceFactor)定义为该结点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有结点的平衡因子只可能是-1.0和1.(此段定义来自严蔚敏的<数据结构(C语言版)>) 红黑树 R-B Tree,全称是Red-B

  • SYN Cookie在Linux内核中的实现

    概述 在目前以IPv4为支撑的网络协议上搭建的网络环境中,SYN Flood是一种非常危险而常见的DoS攻击方式.到目前为止,能够有效防范SYN Flood攻击的手段并不多,而SYN Cookie就是其中最著名的一种.SYN Cookie原理由D. J. Bernstain和 Eric Schenk发明.在很多操作系统上都有各种各样的实现.其中包括Linux.本文就分别介绍一下SYN Flood攻击和SYN Cookie的原理,更重要的是介绍Linux内核中实现SYN Cookie的方式.最后,

  • Linux内核链表实现过程

    关于双链表实现,一般教科书上定义一个双向链表节点的方法如下: 复制代码 代码如下: struct list_node{stuct list_node *pre;stuct list_node *next;ElemType data; } 即一个链表节点包含:一个指向前向节点的指针.一个指向后续节点的指针,以及数据域共三部分.但查看linux内核代码中的list实现时,会发现其与教科书上的方法有很大的差别.来看看linux是如何实现双链表.双链表节点定义 复制代码 代码如下: struct lis

  • 数据结构之红黑树详解

    1.简介 红黑树是一种自平衡二叉查找树.它的统计性能要好于平衡二叉树(AVL树),因此,红黑树在很多地方都有应用.在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持).它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除等操作. 本文介绍了红黑树的基本性质和基本操作. 2.红黑树

随机推荐