浅谈红黑树(C语言代码实现)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈红黑树(C语言代码实现)相关的知识,希望对你有一定的参考价值。

定义:

我们先来看看《算法导论》中的红黑树的定义:“红黑树是许多‘平衡’搜索树的一种,可以保证在最坏的情况下基本动态集合操作的时间复杂度为Olgn)。”

 

性质:

红黑树的性质如下:

1、每个节点是红色的,或者是黑色的。

2、根节点和叶子节点是黑色的。

3、如果一个节点是红色的,那么它的父节点和子节点必须是黑色的。

4、对于每一个节点来说,从该节点到叶子节点的简单路径上,所包含的黑色节点的数量必须一致。

 

节点属性:

红黑树每个节点的基本属性值有五个:

1、color:表示该节点的颜色属性。

2、data:表示该节点的数据域。

3、parent:表示该节点的父节点。

4、left_child:表示该节点的左子结点。

5、right_child:表示该节点的右子结点。

以下代码的红黑树节点属性信息:  

typedef struct rbnode {
    int           data   ;   
    int           color  ;   
    struct rbnode *left_child     ;
    struct rbnode *right_child     ;
    struct rbnode *parent      ;   
} Rbnode;

typedef Rbnode *Rbtree;


插入操作:

插入操作包含以下三个步骤:

1、查找操作。为待插入节点寻找合适的位置。

红黑树的节点的数据域的规律满足平衡二叉树的节点数据与的规律,也就是某节点的左子结点的数据域小于该节点的数据域,同时该节点的右子结点的数据域大于该节点的数据域。

  根据以上数据域的规律可以找到带插入节点的位置,若待插入节点的数据域已经存在于树中,可以选择继续插入,也可以直接返回,该处,我选择若存在就直接返回,若该节点的数据域在该树中不存在,就返回查找到的待测节点的父节点。

  代码如下:

static Rbnode *rbtree_search_auxilary(Rbtree root, int data, Rbnode **parent)

{

    Rbnode *node = NULL, *sparent;

 

    if(NULL == root){

        return NULL;

    }

 

    node= root;

    while(node){

        sparent = node->parent;

        if(data == node->data){

            return node;

        }else if(data > node->data){

            node = node->right_child;

        }else{

            node = node->left_child;

        }

    }

 

    if(parent){

       *parent = sparent;

    }

    return NULL;

}

2、插入待测节点操作。

   由性质4可以得出,为了使新插入节点尽量不破坏红黑树的平衡,新插入节点的颜色必须为红色。

   代码如下:

Rbtree rbtree_insert(Rbtree root, int data)
{
    Rbtree tree  = NULL;
    Rbnode *node =  NULL, *parent = NULL;

    // analyse whether newnode‘s data in this tree, if yes, you need‘t insert it.
    if(root){
        node = rbtree_search_auxilary(root, data, &parent);
        if(node){
            printf("%d already in this tree\n", data);
            return root;
        }
    }
    // insert data in tree;
    node = buy_node(); // function ‘buy_node‘ just malloc a space for new_node;
    node->data = data;
    node->color = RED;
    node->parent = parent;
    node->left_child = node->right_child = NULL;

    if(parent){
        if(parent->left_child){
            parent->left_child = node;
        }else{
            parent->right_child = node;
        }
    }else{
        root = node;
    }

    return rbtree_insert_rebalance(root, node);
}

3、平衡红黑树操作。

   若新插入节点的父节点为黑色节点,那么该树满足红黑树的性质,不需要调整。若新插入节点的父节点为红色节点,此时违背了红黑树的性质3,需要进一步平衡红黑树。

    平衡插入操作有五种情况:

    1.父节点P,叔叔节点U,都是红色;
    2.父节点P是红色,叔叔节点U是黑色或者NULL,且N为右孩子
    3.父节点P是红色,叔叔节点U是黑色或者NULL,且N为左孩子

   代码如下:

static Rbtree rbtree_insert_rebalance(Rbtree root, Rbnode *node)
{
    Rbnode *gparent = NULL, *parent = NULL, *uncle = NULL;

    if(NULL == root || NULL == node){
        return NULL;
    }

    while((parent = node->parent) && RED == parent->color){

        gparent = parent->parent;

        if(gparent){
            if(parent == gparent->left_child){
                uncle = gparent->right_child;
                // case 1,uncle is red;
                if(uncle && RED == uncle->color){
                    parent->color = BLACK;
                    uncle->color = BLACK;
                    gparent->color = RED;
                    node = gparent;
                }else{

                /*case2 & case3, uncle is black.

                 case2: if new_node is parent‘s right_node, becase parent is gparent‘s

                 left_child, this case belongs to inner_insert, so, it is need

                 double_rotate, right_rotate -> left_rotate.

                 case 3: new_node is parent;s left_node, its belongs to outer_insert,

                 just need left_rotate.

                 accrounding foregoing analysis, case2 contains case3.*/

                    if(node == parent->right_child){
                        root = rbtree_rotate_left(root, parent);
                        swap(&parent, &node, sizeof(parent));
                    }

                    parent->color = BLACK;
                    gparent->color = RED;
                    root = rbtree_rotate_right(root, gparent);
                }
            }else{

                // the same as foregoing operating.

                uncle = gparent->left_child;

                if(uncle && RED == uncle->color){
                    uncle->color = BLACK;
                    parent->color = BLACK;
                    gparent->color = RED;
                    node = gparent;
                }else{
                    if(node == parent->left_child){
                        root = rbtree_rotate_right(root, parent);
                        swap(&parent, &node, sizeof(parent));
                    }
                    parent->color = BLACK;
                    gparent->color = RED;
                    root = rbtree_rotate_left(root, gparent);
                }
            }
        }
    }

    root->color = BLACK;
    return root;
}
由于在平衡过程中由旋转(即左旋、右旋)的过程,还有工具函数swap的使用,附上源码:

   旋转的过程可能是我们最头疼的问题,因为搞不懂指针的指向,在这里,我与大家分享一下小技巧,在旋转的过程中我们一步一步来。

   首先,旋转的过程中解决子节点问题。若右旋过程中,节点存在右子结点,将右子结点托管其父节点,作为父节点的左子结点,左旋则相反。

   其次,旋转过程中解决父节点的问题。无论左旋还是右旋其父节点,均都会作为该旋转节点的子节点。

   最后,解决节点本身的问题。节点的父节点是哪个?其父节点为该节点父节点的父节点,若父节点的父节点不存在,该节点为root。节点的子节点是哪个?若右旋过程中,父节点作为其右子结点,左子结点不变;左旋相反。

   左旋右旋的代码如下:

   static Rbtree rbtree_rotate_left(Rbnode *node, Rbtree root)
{
    Rbnode *right = node->right_child;

    // child problem
    if(node->right_child = right->left_child){
        right->left_child->parent = node;
    }   

    // parent problem
    if(right->parent = node->parent){
        if(node == node->parent->right_child){
            node->parent->right_child = right;
        }else{
            node->parent->left_child = right;
        }
    }else{
        root = right;
    }

    // self problem
    right->left_child = node;
    node->parent = right;

    return root;
}

static Rbtree rbtree_rotate_right(Rbnode *node, Rbtree root)
{
    Rbnode *left = node->left_child;

    // child‘s problem
    if(node->left_child = left->right_child){
        left->right_child->parent = node;
    }

    // parent‘s problem
    if(left->parent = node->parent){
        if(node == node->parent->right_child){
            node->parent->right_child = left;
        }else{
            node->parent->left_child = left;
        }
    }else{
        root = left;
    }

    // self‘s problem
    left->right_child = node;
    node->parent = left;

    return root;
}

swap函数源码:

void swap(void *a, void *b, size_t size)
{
    void *tmp;
    tmp = (void *)Malloc(size);
    memcpy(tmp, a, size);
    memcpy(a, b , size);
    memcpy(b, tmp, size);
    free(tmp);
}

删除操作:

   删除操作与插入操作类似也分为三步操作:

   1、找出待删除节点。其查找方法与插入操作的查找节点的方法一致,不再做过多赘述。

   2、若节点存在就删除该节点。

    情况一:待删除节点有两个子节点。

    情况二:待删除节点有一个子节点或者不存在子节点。

    情况一可以转换成情况二,当待删除节点存在两个子节点时,可以用左子树的最大节点或者右子树的最小节点替换该节点,此时,只需要删除左子树的最大节点或者右子树的最小节点即可。

代码如下:

Rbtree rbtree_delete(Rbtree root, int data)
{
    Rbnode *parent = NULL, *child = NULL;
    Rbnode *node   = NULL, *old   = NULL;
    int    color   = RED;

    // find the delete-node
    node = rbtree_search_auxilary(root, data, NULL);
    if(!node){
        fprintf(stderr, "%d does not in the tree\n");
        return NULL;
    }
    // remember the delete-node
    old = node;
    /*
     * case 1 : the delete-node has only-one child, you just
     *      let node‘s parent adapt you child;
     * case 2 : the delete-node has two children, you can find
     *      max-left_child or min-right_child to success your
     *      property & support your parent.
     * Then case 2 can switch to case 1.
     */
    // case 2
    if(node->left_child && node->right_child){
        // we choose its min-right_child
        node = node->right_child;
        node = find_min_node(node);

      // you should solve the min-right_child‘s child & parent
        child = node->right_child;
        parent = node->parent;
        color = node->color;

        // child first
        if(child){
            child->parent = parent;
        }
        // parent sencond
        if(parent){
            if(node == parent->left_child){
                parent->left_child = child;
            }else{
                parent->right_child = child;
            }
        }else{
            root = child;
        }

        if(node->parent == old){
            parent = node;
        }

        // the use min-right_child to replace the delete-node
        node->parent = old->parent;
        node->right_child = old->right_child;
        node->left_child = old->left_child;
        node->color = old->color;


      if(old->parent){
            if(old == old->parent->left_child){
                old->parent->left_child = node;
            }else{
                old->parent->right_child = node;
            }
        }else{
            root = node;
        }
        old->left_child->parent = node;
        if(old->right_child){
            old->right_child->parent = node;
        }
    }else{
       // case 1 : only has one child or has no->child
        if(node->left_child){
            child = node->left_child;
        }else{
            child = node->right_child;
        }
        parent = node->parent;
        color = node->color;

        if(child){
            child->parent = parent;
        }

        if(parent){
            if(node == parent->left_child){
                parent->left_child = child;
            }else{
                parent->right_child = child;
            }
        }else{
            root = child;
        }
    }

    free(old);

    if(BLACK == color){
        return rbtree_delete_rebalance(root, child, parent);
    }

    return root;
}

3、删除节点后平衡红黑树。

   若删除的节点是红色,并没有破坏红黑树的平衡,不需要做平衡操作,若删除节点的黑色节点,是通过该节点的路径比不通过该节点大的路径减少一个黑色节点,违反了红黑树的性质4

   删除操作有以下四种情况:

     1.当前节点是黑色,当前兄弟节点为红色(此时父节点和兄弟节点的子节点为黑色);

      2.当前节点是黑色,且当前节点的兄弟节点是黑色,并且兄弟节点的两个子节点全为黑色;
      3.当前节点是黑色,兄弟节点是黑色,兄弟节点的左孩子是红色,右孩子是黑色;
      4.当前节点是黑色,兄弟节点是黑色,兄弟节点的右孩子是红色,左孩子颜色任意;

     具体平衡操作代码如下:

static Rbtree rbtree_delete_rebalance(Rbtree root, Rbnode *node, Rbnode *parent)
{
     Rbnode *bro = NULL;
     Rbnode *bro_left = NULL, *bro_right = NULL;

     while((NULL == node || BLACK == node->color) && node != root){
         if(node == parent->left_child){
             bro = parent->right_child;

             // case 1 : bro is Red
             if(bro && RED == bro->color){

                 bro->color = BLACK;
                 bro->parent = RED;
                 root = rbtree_rotate_left(parent, root);
                 bro = parent->left_child;

             }   
                 
             if((!bro->left_child || BLACK == bro->left_child->color) &&
                (!bro->right_child || BLACK == bro->right_child->color)){
                 // case 2 : bro is BLACK & its children are all BLACK
                 bro->color = RED;
                 node = parent;
                 parent = node->parent;
             }else{
                 // case 3 : bro is BLACK & its left_child is RED & bro_right is BLACK
                 if(NULL == bro->right_child || BLACK == bro->right_child->color){
                     if(bro_left = bro->left_child){
                         bro_left->color = BLACK;
                     }
                     bro->color = RED;
                     root = rbtree_rotate_right(bro, root);
                     bro = parent->right_child;
                 }   
                     
                 // case 4 : bro is BLACK & its right_child is RED;
                 bro->color = parent->color;
                 parent->color = BLACK;
                 if(bro->right_child){
                     bro->right_child->color = BLACK;
                 }
                 root = rbtree_rotate_left(parent, root);
                 node = root;
                 break;
             }
         }else{
             bro = parent->left_child;

             //  case 1
             if(bro && RED == bro->color){
                 bro->color = BLACK;
                 bro->parent = RED;
                 root = rbtree_rotate_right(parent, root);
                 bro = parent->left_child;
             }

             // case 2
             if((!bro->left_child || BLACK == bro->left_child->color) &&
                (!bro->right_child) || BLACK == bro->right_child->color){
                 bro->color = RED;
                 node = parent;
                 parent = node->parent;
             }else{

                 // case 3
                 if(!bro->right_child || BLACK == bro->right_child->color){
                     if(bro_left = bro->left_child){
                         bro_left->color = BLACK;
                     }
                     bro->color = RED;
                     root = rbtree_insert_rebalance(bro, root);
                     node = parent->left_child;
                 }

                 // case 4
                 bro->color = parent->color;
                 parent->color = BLACK;
                 if(bro->right_child){
                     bro->right_child->color = BLACK;
                 }
                 root = rbtree_rotate_left(parent,root);
                 node = root;
                 break;
             }
         }
     }

     if(node){
         node->color = BLACK;
     }

     return root;
}

由于以上代码中牵扯寻找左子树的最大节点,右子树的最小节点,以下是实现源码:

Rbnode *find_min_node(Rbtree root)
{
    Rbnode *node = root;

    if(NULL == root){
        return NULL;
    }

    while(node->left_child){
      


  node = node->left_child;
    }

    return node;
}

Rbnode *find_max_node(Rbtree root)
{
    Rbnode *node = root;

    if(NULL == root){
        return NULL;
    }

    while(node->right_child){
        node = node->right_child;
    }

    return node;
}

  以上是本人对红黑树的简单理解,如有不妥之处,欢迎大家指出。



 


本文出自 “11219885” 博客,请务必保留此出处http://11229885.blog.51cto.com/11219885/1746148

以上是关于浅谈红黑树(C语言代码实现)的主要内容,如果未能解决你的问题,请参考以下文章

浅谈红黑树的添加删除操作

浅谈红黑树——java面试拦路虎

红黑树 实现

红黑树插入与删除完整代码(dart语言实现)

C语言中的红黑树实现

算法导论 之 红黑树 - 打印销毁 - 非递归[C语言]