红黑树(二)之 C语言的实现

https://www.cnblogs.com/skywang12345/p/3624177.html

 

概要

红黑树在日常的使用中比较常用,例如Java的TreeMapTreeSet,C++的STL,以及Linux内核中都有用到。之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现代码,后序章节再分别给出C++和Java版本的实现。还是那句话,三种实现原理相同,择其一了解即可;若文章有错误或不足的地方,望不吝指出!

目录
1. 红黑树的介绍
2. 红黑树的C实现(代码说明)
3. 红黑树的C实现(完整源码)
4. 红黑树的C测试程序

 

红黑树的介绍

红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树。
红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。
除了具备该特性之外,红黑树还包括许多额外的信息。

红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black)。
红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

关于它的特性,需要注意的是:
第一,特性(3)中的叶子节点,是只为空(NIL或null)的节点。
第二,特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。

红黑树示意图如下:

 

红黑树的C实现(代码说明)

红黑树的基本操作是添加删除旋转。在对红黑树进行添加或删除后,会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋右旋。下面分别对旋转(左旋和右旋)、添加、删除进行介绍。

1. 基本定义

复制代码
#define RED        0    // 红色节点
#define BLACK    1    // 黑色节点

typedef int Type;

// 红黑树的节点
typedef struct RBTreeNode{
    unsigned char color;        // 颜色(RED 或 BLACK)
    Type   key;                    // 关键字(键值)
    struct RBTreeNode *left;    // 左孩子
    struct RBTreeNode *right;    // 右孩子
    struct RBTreeNode *parent;    // 父结点
}Node, *RBTree;

// 红黑树的根
typedef struct rb_root{
    Node *node;
}RBRoot;
复制代码

RBTreeNode是红黑树的节点类,RBRoot是红黑树的根。

 

2. 左旋

对x进行左旋,意味着"将x变成一个左节点"。

左旋的实现代码(C语言)

复制代码
/* 
 * 对红黑树的节点(x)进行左旋转
 *
 * 左旋示意图(对节点x进行左旋):
 *      px                              px
 *     /                               /
 *    x                               y                
 *   /  \      --(左旋)-->           / \                #
 *  lx   y                          x  ry     
 *     /   \                       /  \
 *    ly   ry                     lx  ly  
 *
 *
 */
static void rbtree_left_rotate(RBRoot *root, Node *x)
{
    // 设置x的右孩子为y
    Node *y = x->right;

    // 将 “y的左孩子” 设为 “x的右孩子”;
    // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
    x->right = y->left;
    if (y->left != NULL)
        y->left->parent = x;

    // 将 “x的父亲” 设为 “y的父亲”
    y->parent = x->parent;

    if (x->parent == NULL)
    {
        //tree = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
        root->node = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
    }
    else
    {
        if (x->parent->left == x)
            x->parent->left = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
        else
            x->parent->right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
    }
    
    // 将 “x” 设为 “y的左孩子”
    y->left = x;
    // 将 “x的父节点” 设为 “y”
    x->parent = y;
}
复制代码

 

3. 右旋


对y进行左旋,意味着"将y变成一个右节点"。

右旋的实现代码(C语言)

复制代码
/* 
 * 对红黑树的节点(y)进行右旋转
 *
 * 右旋示意图(对节点y进行左旋):
 *            py                               py
 *           /                                /
 *          y                                x                  
 *         /  \      --(右旋)-->            /  \                     #
 *        x   ry                           lx   y  
 *       / \                                   / \                   #
 *      lx  rx                                rx  ry
 * 
 */
static void rbtree_right_rotate(RBRoot *root, Node *y)
{
    // 设置x是当前节点的左孩子。
    Node *x = y->left;

    // 将 “x的右孩子” 设为 “y的左孩子”;
    // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
    y->left = x->right;
    if (x->right != NULL)
        x->right->parent = y;

    // 将 “y的父亲” 设为 “x的父亲”
    x->parent = y->parent;

    if (y->parent == NULL) 
    {
        //tree = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
        root->node = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
    }
    else
    {
        if (y == y->parent->right)
            y->parent->right = x;    // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
        else
            y->parent->left = x;    // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
    }

    // 将 “y” 设为 “x的右孩子”
    x->right = y;

    // 将 “y的父节点” 设为 “x”
    y->parent = x;
}
复制代码

 

4. 添加

将一个节点(z)插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过"旋转和重新着色"等一系列操作来修正该树,使之重新成为一颗红黑树。详细描述如下:

第一步: 将红黑树当作一颗二叉查找树,将节点插入。
       红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
       好吧?那接下来,我们就来想方设法的旋转以及重新着色,使这颗树重新成为红黑树!

第二步:将插入的节点着色为"红色"。
       为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
       将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。o(∩∩)o...哈哈

第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
       第二步中,将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
       对于"特性(1)",显然不会违背了。因为我们已经将它涂成红色了。
       对于"特性(2)",显然也不会违背。在第一步中,我们是将红黑树当作二叉查找树,然后执行的插入操作。而根据二叉查找数的特点,插入操作不会改变根节点。所以,根节点仍然是黑色。
       对于"特性(3)",显然不会违背了。这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
       对于"特性(4)",是有可能违背的!
那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。


添加操作的实现代码(C语言)

复制代码
/*
 * 添加节点:将节点(node)插入到红黑树中
 *
 * 参数说明:
 *     root 红黑树的根
 *     node 插入的结点        // 对应《算法导论》中的z
 */
static void rbtree_insert(RBRoot *root, Node *node)
{
    Node *y = NULL;
    Node *x = root->node;

    // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
    while (x != NULL)
    {
        y = x;
        if (node->key < x->key)
            x = x->left;
        else
            x = x->right;
    }
    rb_parent(node) = y;

    if (y != NULL)
    {
        if (node->key < y->key)
            y->left = node;                // 情况2:若“node所包含的值” < “y所包含的值”,则将node设为“y的左孩子”
        else
            y->right = node;            // 情况3:(“node所包含的值” >= “y所包含的值”)将node设为“y的右孩子” 
    }
    else
    {
        root->node = node;                // 情况1:若y是空节点,则将node设为根
    }

    // 2. 设置节点的颜色为红色
    node->color = RED;

    // 3. 将它重新修正为一颗二叉查找树
    rbtree_insert_fixup(root, node);
}
复制代码

rbtree_insert(root, node)的作用是将"node"节点插入到红黑树中。其中,root是根,node是被插入节点。
rbtree_insert(root, node)是参考《算法导论》中红黑树的插入函数的伪代码进行实现的。


添加修正操作的实现代码(C语言)

复制代码
/*
 * 红黑树插入修正函数
 *
 * 在向红黑树中插入节点之后(失去平衡),再调用该函数;
 * 目的是将它重新塑造成一颗红黑树。
 *
 * 参数说明:
 *     root 红黑树的根
 *     node 插入的结点        // 对应《算法导论》中的z
 */
static void rbtree_insert_fixup(RBRoot *root, Node *node)
{
    Node *parent, *gparent;

    // 若“父节点存在,并且父节点的颜色是红色”
    while ((parent = rb_parent(node)) && rb_is_red(parent))
    {
        gparent = rb_parent(parent);

        //若“父节点”是“祖父节点的左孩子”
        if (parent == gparent->left)
        {
            // Case 1条件:叔叔节点是红色
            {
                Node *uncle = gparent->right;
                if (uncle && rb_is_red(uncle))
                {
                    rb_set_black(uncle);
                    rb_set_black(parent);
                    rb_set_red(gparent);
                    node = gparent;
                    continue;
                }
            }

            // Case 2条件:叔叔是黑色,且当前节点是右孩子
            if (parent->right == node)
            {
                Node *tmp;
                rbtree_left_rotate(root, parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3条件:叔叔是黑色,且当前节点是左孩子。
            rb_set_black(parent);
            rb_set_red(gparent);
            rbtree_right_rotate(root, gparent);
        } 
        else//若“z的父节点”是“z的祖父节点的右孩子”
        {
            // Case 1条件:叔叔节点是红色
            {
                Node *uncle = gparent->left;
                if (uncle && rb_is_red(uncle))
                {
                    rb_set_black(uncle);
                    rb_set_black(parent);
                    rb_set_red(gparent);
                    node = gparent;
                    continue;
                }
            }

            // Case 2条件:叔叔是黑色,且当前节点是左孩子
            if (parent->left == node)
            {
                Node *tmp;
                rbtree_right_rotate(root, parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3条件:叔叔是黑色,且当前节点是右孩子。
            rb_set_black(parent);
            rb_set_red(gparent);
            rbtree_left_rotate(root, gparent);
        }
    }

    // 将根节点设为黑色
    rb_set_black(root->node);
}
复制代码

rbtree_insert_fixup(root, node)的作用是对应"上面所讲的第三步"。

 

5. 删除操作

将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。详细描述如下:

第一步:将红黑树当作一颗二叉查找树,将节点删除。
       这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。

第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。


删除操作的实现代码(C语言)

复制代码
/* 
 * 删除结点
 *
 * 参数说明:
 *     tree 红黑树的根结点
 *     node 删除的结点
 */
void rbtree_delete(RBRoot *root, Node *node)
{
    Node *child, *parent;
    int color;

    // 被删除节点的"左右孩子都不为空"的情况。
    if ( (node->left!=NULL) && (node->right!=NULL) ) 
    {
        // 被删节点的后继节点。(称为"取代节点")
        // 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。
        Node *replace = node;

        // 获取后继节点
        replace = replace->right;
        while (replace->left != NULL)
            replace = replace->left;

        // "node节点"不是根节点(只有根节点不存在父节点)
        if (rb_parent(node))
        {
            if (rb_parent(node)->left == node)
                rb_parent(node)->left = replace;
            else
                rb_parent(node)->right = replace;
        } 
        else 
            // "node节点"是根节点,更新根节点。
            root->node = replace;

        // child是"取代节点"的右孩子,也是需要"调整的节点"。
        // "取代节点"肯定不存在左孩子!因为它是一个后继节点。
        child = replace->right;
        parent = rb_parent(replace);
        // 保存"取代节点"的颜色
        color = rb_color(replace);

        // "被删除节点"是"它的后继节点的父节点"
        if (parent == node)
        {
            parent = replace;
        } 
        else
        {
            // child不为空
            if (child)
                rb_set_parent(child, parent);
            parent->left = child;

            replace->right = node->right;
            rb_set_parent(node->right, replace);
        }

        replace->parent = node->parent;
        replace->color = node->color;
        replace->left = node->left;
        node->left->parent = replace;

        if (color == BLACK)
            rbtree_delete_fixup(root, child, parent);
        free(node);

        return ;
    }

    if (node->left !=NULL)
        child = node->left;
    else 
        child = node->right;

    parent = node->parent;
    // 保存"取代节点"的颜色
    color = node->color;

    if (child)
        child->parent = parent;

    // "node节点"不是根节点
    if (parent)
    {
        if (parent->left == node)
            parent->left = child;
        else
            parent->right = child;
    }
    else
        root->node = child;

    if (color == BLACK)
        rbtree_delete_fixup(root, child, parent);
    free(node);
}
复制代码

rbtree_delete(root, node)的作用是将"node"节点插入到红黑树中。其中,root是根,node是被插入节点。


删除修正操作的实现代码(C语言)

复制代码
/*
 * 红黑树删除修正函数
 *
 * 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
 * 目的是将它重新塑造成一颗红黑树。
 *
 * 参数说明:
 *     root 红黑树的根
 *     node 待修正的节点
 */
static void rbtree_delete_fixup(RBRoot *root, Node *node, Node *parent)
{
    Node *other;

    while ((!node || rb_is_black(node)) && node != root->node)
    {
        if (parent->left == node)
        {
            other = parent->right;
            if (rb_is_red(other))
            {
                // Case 1: x的兄弟w是红色的  
                rb_set_black(other);
                rb_set_red(parent);
                rbtree_left_rotate(root, parent);
                other = parent->right;
            }
            if ((!other->left || rb_is_black(other->left)) &&
                (!other->right || rb_is_black(other->right)))
            {
                // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的  
                rb_set_red(other);
                node = parent;
                parent = rb_parent(node);
            }
            else
            {
                if (!other->right || rb_is_black(other->right))
                {
                    // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。  
                    rb_set_black(other->left);
                    rb_set_red(other);
                    rbtree_right_rotate(root, other);
                    other = parent->right;
                }
                // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
                rb_set_color(other, rb_color(parent));
                rb_set_black(parent);
                rb_set_black(other->right);
                rbtree_left_rotate(root, parent);
                node = root->node;
                break;
            }
        }
        else
        {
            other = parent->left;
            if (rb_is_red(other))
            {
                // Case 1: x的兄弟w是红色的  
                rb_set_black(other);
                rb_set_red(parent);
                rbtree_right_rotate(root, parent);
                other = parent->left;
            }
            if ((!other->left || rb_is_black(other->left)) &&
                (!other->right || rb_is_black(other->right)))
            {
                // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的  
                rb_set_red(other);
                node = parent;
                parent = rb_parent(node);
            }
            else
            {
                if (!other->left || rb_is_black(other->left))
                {
                    // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。  
                    rb_set_black(other->right);
                    rb_set_red(other);
                    rbtree_left_rotate(root, other);
                    other = parent->left;
                }
                // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
                rb_set_color(other, rb_color(parent));
                rb_set_black(parent);
                rb_set_black(other->left);
                rbtree_right_rotate(root, parent);
                node = root->node;
                break;
            }
        }
    }
    if (node)
        rb_set_black(node);
}
复制代码

rbtree_delete_fixup(root, node, parent)是对应"上面所讲的第三步"。

 

红黑树的C实现(完整源码)

下面是红黑数实现的完整代码和相应的测试程序。
(1) 除了上面所说的"左旋"、"右旋"、"添加"、"删除"等基本操作之后,还实现了"遍历"、"查找"、"打印"、"最小值"、"最大值"、"创建"、"销毁"等接口。
(2) 函数接口分为内部接口和外部接口。内部接口是static函数,外部接口则是非static函数,外部接口都在.h头文件中表明了。
(3) 测试代码中提供了"插入"和"删除"动作的检测开关。默认是关闭的,打开方法可以参考"代码中的说明"。建议在打开开关后,在草稿上自己动手绘制一下红黑树。

红黑树的实现文件(rbtree.h)

复制代码
 1 #ifndef _RED_BLACK_TREE_H_
 2 #define _RED_BLACK_TREE_H_
 3 
 4 #define RED        0    // 红色节点
 5 #define BLACK    1    // 黑色节点
 6 
 7 typedef int Type;
 8 
 9 // 红黑树的节点
10 typedef struct RBTreeNode{
11     unsigned char color;        // 颜色(RED 或 BLACK)
12     Type   key;                    // 关键字(键值)
13     struct RBTreeNode *left;    // 左孩子
14     struct RBTreeNode *right;    // 右孩子
15     struct RBTreeNode *parent;    // 父结点
16 }Node, *RBTree;
17 
18 // 红黑树的根
19 typedef struct rb_root{
20     Node *node;
21 }RBRoot;
22 
23 // 创建红黑树,返回"红黑树的根"!
24 RBRoot* create_rbtree();
25 
26 // 销毁红黑树
27 void destroy_rbtree(RBRoot *root);
28 
29 // 将结点插入到红黑树中。插入成功,返回0;失败返回-1。
30 int insert_rbtree(RBRoot *root, Type key);
31 
32 // 删除结点(key为节点的值)
33 void delete_rbtree(RBRoot *root, Type key);
34 
35 
36 // 前序遍历"红黑树"
37 void preorder_rbtree(RBRoot *root);
38 // 中序遍历"红黑树"
39 void inorder_rbtree(RBRoot *root);
40 // 后序遍历"红黑树"
41 void postorder_rbtree(RBRoot *root);
42 
43 // (递归实现)查找"红黑树"中键值为key的节点。找到的话,返回0;否则,返回-1。
44 int rbtree_search(RBRoot *root, Type key);
45 // (非递归实现)查找"红黑树"中键值为key的节点。找到的话,返回0;否则,返回-1。
46 int iterative_rbtree_search(RBRoot *root, Type key);
47 
48 // 返回最小结点的值(将值保存到val中)。找到的话,返回0;否则返回-1。
49 int rbtree_minimum(RBRoot *root, int *val);
50 // 返回最大结点的值(将值保存到val中)。找到的话,返回0;否则返回-1。
51 int rbtree_maximum(RBRoot *root, int *val);
52 
53 // 打印红黑树
54 void print_rbtree(RBRoot *root);
55 
56 #endif
复制代码

红黑树的实现文件(rbtree.c)

复制代码
  1 /**
  2  * C语言实现的红黑树(Red Black Tree)
  3  *
  4  * @author skywang
  5  * @date 2013/11/18
  6  */
  7 
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include "rbtree.h"
 11 
 12 #define rb_parent(r)   ((r)->parent)
 13 #define rb_color(r) ((r)->color)
 14 #define rb_is_red(r)   ((r)->color==RED)
 15 #define rb_is_black(r)  ((r)->color==BLACK)
 16 #define rb_set_black(r)  do { (r)->color = BLACK; } while (0)
 17 #define rb_set_red(r)  do { (r)->color = RED; } while (0)
 18 #define rb_set_parent(r,p)  do { (r)->parent = (p); } while (0)
 19 #define rb_set_color(r,c)  do { (r)->color = (c); } while (0)
 20 
 21 /*
 22  * 创建红黑树,返回"红黑树的根"!
 23  */
 24 RBRoot* create_rbtree()
 25 {
 26     RBRoot *root = (RBRoot *)malloc(sizeof(RBRoot));
 27     root->node = NULL;
 28 
 29     return root;
 30 }
 31 
 32 /*
 33  * 前序遍历"红黑树"
 34  */
 35 static void preorder(RBTree tree)
 36 {
 37     if(tree != NULL)
 38     {
 39         printf("%d ", tree->key);
 40         preorder(tree->left);
 41         preorder(tree->right);
 42     }
 43 }
 44 void preorder_rbtree(RBRoot *root) 
 45 {
 46     if (root)
 47         preorder(root->node);
 48 }
 49 
 50 /*
 51  * 中序遍历"红黑树"
 52  */
 53 static void inorder(RBTree tree)
 54 {
 55     if(tree != NULL)
 56     {
 57         inorder(tree->left);
 58         printf("%d ", tree->key);
 59         inorder(tree->right);
 60     }
 61 }
 62 
 63 void inorder_rbtree(RBRoot *root) 
 64 {
 65     if (root)
 66         inorder(root->node);
 67 }
 68 
 69 /*
 70  * 后序遍历"红黑树"
 71  */
 72 static void postorder(RBTree tree)
 73 {
 74     if(tree != NULL)
 75     {
 76         postorder(tree->left);
 77         postorder(tree->right);
 78         printf("%d ", tree->key);
 79     }
 80 }
 81 
 82 void postorder_rbtree(RBRoot *root)
 83 {
 84     if (root)
 85         postorder(root->node);
 86 }
 87 
 88 /*
 89  * (递归实现)查找"红黑树x"中键值为key的节点
 90  */
 91 static Node* search(RBTree x, Type key)
 92 {
 93     if (x==NULL || x->key==key)
 94         return x;
 95 
 96     if (key < x->key)
 97         return search(x->left, key);
 98     else
 99         return search(x->right, key);
100 }
101 int rbtree_search(RBRoot *root, Type key)
102 {
103     if (root)
104         return search(root->node, key)? 0 : -1;
105 }
106 
107 /*
108  * (非递归实现)查找"红黑树x"中键值为key的节点
109  */
110 static Node* iterative_search(RBTree x, Type key)
111 {
112     while ((x!=NULL) && (x->key!=key))
113     {
114         if (key < x->key)
115             x = x->left;
116         else
117             x = x->right;
118     }
119 
120     return x;
121 }
122 int iterative_rbtree_search(RBRoot *root, Type key)
123 {
124     if (root)
125         return iterative_search(root->node, key) ? 0 : -1;
126 }
127 
128 /* 
129  * 查找最小结点:返回tree为根结点的红黑树的最小结点。
130  */
131 static Node* minimum(RBTree tree)
132 {
133     if (tree == NULL)
134         return NULL;
135 
136     while(tree->left != NULL)
137         tree = tree->left;
138     return tree;
139 }
140 
141 int rbtree_minimum(RBRoot *root, int *val)
142 {
143     Node *node;
144 
145     if (root)
146         node = minimum(root->node);
147 
148     if (node == NULL)
149         return -1;
150 
151     *val = node->key;
152     return 0;
153 }
154  
155 /* 
156  * 查找最大结点:返回tree为根结点的红黑树的最大结点。
157  */
158 static Node* maximum(RBTree tree)
159 {
160     if (tree == NULL)
161         return NULL;
162 
163     while(tree->right != NULL)
164         tree = tree->right;
165     return tree;
166 }
167 
168 int rbtree_maximum(RBRoot *root, int *val)
169 {
170     Node *node;
171 
172     if (root)
173         node = maximum(root->node);
174 
175     if (node == NULL)
176         return -1;
177 
178     *val = node->key;
179     return 0;
180 }
181 
182 /* 
183  * 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
184  */
185 static Node* rbtree_successor(RBTree x)
186 {
187     // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
188     if (x->right != NULL)
189         return minimum(x->right);
190 
191     // 如果x没有右孩子。则x有以下两种可能:
192     // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
193     // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
194     Node* y = x->parent;
195     while ((y!=NULL) && (x==y->right))
196     {
197         x = y;
198         y = y->parent;
199     }
200 
201     return y;
202 }
203  
204 /* 
205  * 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。
206  */
207 static Node* rbtree_predecessor(RBTree x)
208 {
209     // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
210     if (x->left != NULL)
211         return maximum(x->left);
212 
213     // 如果x没有左孩子。则x有以下两种可能:
214     // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
215     // (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
216     Node* y = x->parent;
217     while ((y!=NULL) && (x==y->left))
218     {
219         x = y;
220         y = y->parent;
221     }
222 
223     return y;
224 }
225 
226 /* 
227  * 对红黑树的节点(x)进行左旋转
228  *
229  * 左旋示意图(对节点x进行左旋):
230  *      px                              px
231  *     /                               /
232  *    x                               y                
233  *   /  \      --(左旋)-->           / \                #
234  *  lx   y                          x  ry     
235  *     /   \                       /  \
236  *    ly   ry                     lx  ly  
237  *
238  *
239  */
240 static void rbtree_left_rotate(RBRoot *root, Node *x)
241 {
242     // 设置x的右孩子为y
243     Node *y = x->right;
244 
245     // 将 “y的左孩子” 设为 “x的右孩子”;
246     // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
247     x->right = y->left;
248     if (y->left != NULL)
249         y->left->parent = x;
250 
251     // 将 “x的父亲” 设为 “y的父亲”
252     y->parent = x->parent;
253 
254     if (x->parent == NULL)
255     {
256         //tree = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
257         root->node = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
258     }
259     else
260     {
261         if (x->parent->left == x)
262             x->parent->left = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
263         else
264             x->parent->right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
265     }
266     
267     // 将 “x” 设为 “y的左孩子”
268     y->left = x;
269     // 将 “x的父节点” 设为 “y”
270     x->parent = y;
271 }
272 
273 /* 
274  * 对红黑树的节点(y)进行右旋转
275  *
276  * 右旋示意图(对节点y进行左旋):
277  *            py                               py
278  *           /                                /
279  *          y                                x                  
280  *         /  \      --(右旋)-->            /  \                     #
281  *        x   ry                           lx   y  
282  *       / \                                   / \                   #
283  *      lx  rx                                rx  ry
284  * 
285  */
286 static void rbtree_right_rotate(RBRoot *root, Node *y)
287 {
288     // 设置x是当前节点的左孩子。
289     Node *x = y->left;
290 
291     // 将 “x的右孩子” 设为 “y的左孩子”;
292     // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
293     y->left = x->right;
294     if (x->right != NULL)
295         x->right->parent = y;
296 
297     // 将 “y的父亲” 设为 “x的父亲”
298     x->parent = y->parent;
299 
300     if (y->parent == NULL) 
301     {
302         //tree = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
303         root->node = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点
304     }
305     else
306     {
307         if (y == y->parent->right)
308             y->parent->right = x;    // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
309         else
310             y->parent->left = x;    // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
311     }
312 
313     // 将 “y” 设为 “x的右孩子”
314     x->right = y;
315 
316     // 将 “y的父节点” 设为 “x”
317     y->parent = x;
318 }
319 
320 /*
321  * 红黑树插入修正函数
322  *
323  * 在向红黑树中插入节点之后(失去平衡),再调用该函数;
324  * 目的是将它重新塑造成一颗红黑树。
325  *
326  * 参数说明:
327  *     root 红黑树的根
328  *     node 插入的结点        // 对应《算法导论》中的z
329  */
330 static void rbtree_insert_fixup(RBRoot *root, Node *node)
331 {
332     Node *parent, *gparent;
333 
334     // 若“父节点存在,并且父节点的颜色是红色”
335     while ((parent = rb_parent(node)) && rb_is_red(parent))
336     {
337         gparent = rb_parent(parent);
338 
339         //若“父节点”是“祖父节点的左孩子”
340         if (parent == gparent->left)
341         {
342             // Case 1条件:叔叔节点是红色
343             {
344                 Node *uncle = gparent->right;
345                 if (uncle && rb_is_red(uncle))
346                 {
347                     rb_set_black(uncle);
348                     rb_set_black(parent);
349                     rb_set_red(gparent);
350                     node = gparent;
351                     continue;
352                 }
353             }
354 
355             // Case 2条件:叔叔是黑色,且当前节点是右孩子
356             if (parent->right == node)
357             {
358                 Node *tmp;
359                 rbtree_left_rotate(root, parent);
360                 tmp = parent;
361                 parent = node;
362                 node = tmp;
363             }
364 
365             // Case 3条件:叔叔是黑色,且当前节点是左孩子。
366             rb_set_black(parent);
367             rb_set_red(gparent);
368             rbtree_right_rotate(root, gparent);
369         } 
370         else//若“z的父节点”是“z的祖父节点的右孩子”
371         {
372             // Case 1条件:叔叔节点是红色
373             {
374                 Node *uncle = gparent->left;
375                 if (uncle && rb_is_red(uncle))
376                 {
377                     rb_set_black(uncle);
378                     rb_set_black(parent);
379                     rb_set_red(gparent);
380                     node = gparent;
381                     continue;
382                 }
383             }
384 
385             // Case 2条件:叔叔是黑色,且当前节点是左孩子
386             if (parent->left == node)
387             {
388                 Node *tmp;
389                 rbtree_right_rotate(root, parent);
390                 tmp = parent;
391                 parent = node;
392                 node = tmp;
393             }
394 
395             // Case 3条件:叔叔是黑色,且当前节点是右孩子。
396             rb_set_black(parent);
397             rb_set_red(gparent);
398             rbtree_left_rotate(root, gparent);
399         }
400     }
401 
402     // 将根节点设为黑色
403     rb_set_black(root->node);
404 }
405 
406 /*
407  * 添加节点:将节点(node)插入到红黑树中
408  *
409  * 参数说明:
410  *     root 红黑树的根
411  *     node 插入的结点        // 对应《算法导论》中的z
412  */
413 static void rbtree_insert(RBRoot *root, Node *node)
414 {
415     Node *y = NULL;
416     Node *x = root->node;
417 
418     // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
419     while (x != NULL)
420     {
421         y = x;
422         if (node->key < x->key)
423             x = x->left;
424         else
425             x = x->right;
426     }
427     rb_parent(node) = y;
428 
429     if (y != NULL)
430     {
431         if (node->key < y->key)
432             y->left = node;                // 情况2:若“node所包含的值” < “y所包含的值”,则将node设为“y的左孩子”
433         else
434             y->right = node;            // 情况3:(“node所包含的值” >= “y所包含的值”)将node设为“y的右孩子” 
435     }
436     else
437     {
438         root->node = node;                // 情况1:若y是空节点,则将node设为根
439     }
440 
441     // 2. 设置节点的颜色为红色
442     node->color = RED;
443 
444     // 3. 将它重新修正为一颗二叉查找树
445     rbtree_insert_fixup(root, node);
446 }
447 
448 /*
449  * 创建结点
450  *
451  * 参数说明:
452  *     key 是键值。
453  *     parent 是父结点。
454  *     left 是左孩子。
455  *     right 是右孩子。
456  */
457 static Node* create_rbtree_node(Type key, Node *parent, Node *left, Node* right)
458 {
459     Node* p;
460 
461     if ((p = (Node *)malloc(sizeof(Node))) == NULL)
462         return NULL;
463     p->key = key;
464     p->left = left;
465     p->right = right;
466     p->parent = parent;
467     p->color = BLACK; // 默认为黑色
468 
469     return p;
470 }
471 
472 /* 
473  * 新建结点(节点键值为key),并将其插入到红黑树中
474  *
475  * 参数说明:
476  *     root 红黑树的根
477  *     key 插入结点的键值
478  * 返回值:
479  *     0,插入成功
480  *     -1,插入失败
481  */
482 int insert_rbtree(RBRoot *root, Type key)
483 {
484     Node *node;    // 新建结点
485 
486     // 不允许插入相同键值的节点。
487     // (若想允许插入相同键值的节点,注释掉下面两句话即可!)
488     if (search(root->node, key) != NULL)
489         return -1;
490 
491     // 如果新建结点失败,则返回。
492     if ((node=create_rbtree_node(key, NULL, NULL, NULL)) == NULL)
493         return -1;
494 
495     rbtree_insert(root, node);
496 
497     return 0;
498 }
499 
500 /*
501  * 红黑树删除修正函数
502  *
503  * 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
504  * 目的是将它重新塑造成一颗红黑树。
505  *
506  * 参数说明:
507  *     root 红黑树的根
508  *     node 待修正的节点
509  */
510 static void rbtree_delete_fixup(RBRoot *root, Node *node, Node *parent)
511 {
512     Node *other;
513 
514     while ((!node || rb_is_black(node)) && node != root->node)
515     {
516         if (parent->left == node)
517         {
518             other = parent->right;
519             if (rb_is_red(other))
520             {
521                 // Case 1: x的兄弟w是红色的  
522                 rb_set_black(other);
523                 rb_set_red(parent);
524                 rbtree_left_rotate(root, parent);
525                 other = parent->right;
526             }
527             if ((!other->left || rb_is_black(other->left)) &&
528                 (!other->right || rb_is_black(other->right)))
529             {
530                 // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的  
531                 rb_set_red(other);
532                 node = parent;
533                 parent = rb_parent(node);
534             }
535             else
536             {
537                 if (!other->right || rb_is_black(other->right))
538                 {
539                     // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。  
540                     rb_set_black(other->left);
541                     rb_set_red(other);
542                     rbtree_right_rotate(root, other);
543                     other = parent->right;
544                 }
545                 // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
546                 rb_set_color(other, rb_color(parent));
547                 rb_set_black(parent);
548                 rb_set_black(other->right);
549                 rbtree_left_rotate(root, parent);
550                 node = root->node;
551                 break;
552             }
553         }
554         else
555         {
556             other = parent->left;
557             if (rb_is_red(other))
558             {
559                 // Case 1: x的兄弟w是红色的  
560                 rb_set_black(other);
561                 rb_set_red(parent);
562                 rbtree_right_rotate(root, parent);
563                 other = parent->left;
564             }
565             if ((!other->left || rb_is_black(other->left)) &&
566                 (!other->right || rb_is_black(other->right)))
567             {
568                 // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的  
569                 rb_set_red(other);
570                 node = parent;
571                 parent = rb_parent(node);
572             }
573             else
574             {
575                 if (!other->left || rb_is_black(other->left))
576                 {
577                     // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。  
578                     rb_set_black(other->right);
579                     rb_set_red(other);
580                     rbtree_left_rotate(root, other);
581                     other = parent->left;
582                 }
583                 // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
584                 rb_set_color(other, rb_color(parent));
585                 rb_set_black(parent);
586                 rb_set_black(other->left);
587                 rbtree_right_rotate(root, parent);
588                 node = root->node;
589                 break;
590             }
591         }
592     }
593     if (node)
594         rb_set_black(node);
595 }
596 
597 /* 
598  * 删除结点
599  *
600  * 参数说明:
601  *     tree 红黑树的根结点
602  *     node 删除的结点
603  */
604 void rbtree_delete(RBRoot *root, Node *node)
605 {
606     Node *child, *parent;
607     int color;
608 
609     // 被删除节点的"左右孩子都不为空"的情况。
610     if ( (node->left!=NULL) && (node->right!=NULL) ) 
611     {
612         // 被删节点的后继节点。(称为"取代节点")
613         // 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。
614         Node *replace = node;
615 
616         // 获取后继节点
617         replace = replace->right;
618         while (replace->left != NULL)
619             replace = replace->left;
620 
621         // "node节点"不是根节点(只有根节点不存在父节点)
622         if (rb_parent(node))
623         {
624             if (rb_parent(node)->left == node)
625                 rb_parent(node)->left = replace;
626             else
627                 rb_parent(node)->right = replace;
628         } 
629         else 
630             // "node节点"是根节点,更新根节点。
631             root->node = replace;
632 
633         // child是"取代节点"的右孩子,也是需要"调整的节点"。
634         // "取代节点"肯定不存在左孩子!因为它是一个后继节点。
635         child = replace->right;
636         parent = rb_parent(replace);
637         // 保存"取代节点"的颜色
638         color = rb_color(replace);
639 
640         // "被删除节点"是"它的后继节点的父节点"
641         if (parent == node)
642         {
643             parent = replace;
644         } 
645         else
646         {
647             // child不为空
648             if (child)
649                 rb_set_parent(child, parent);
650             parent->left = child;
651 
652             replace->right = node->right;
653             rb_set_parent(node->right, replace);
654         }
655 
656         replace->parent = node->parent;
657         replace->color = node->color;
658         replace->left = node->left;
659         node->left->parent = replace;
660 
661         if (color == BLACK)
662             rbtree_delete_fixup(root, child, parent);
663         free(node);
664 
665         return ;
666     }
667 
668     if (node->left !=NULL)
669         child = node->left;
670     else 
671         child = node->right;
672 
673     parent = node->parent;
674     // 保存"取代节点"的颜色
675     color = node->color;
676 
677     if (child)
678         child->parent = parent;
679 
680     // "node节点"不是根节点
681     if (parent)
682     {
683         if (parent->left == node)
684             parent->left = child;
685         else
686             parent->right = child;
687     }
688     else
689         root->node = child;
690 
691     if (color == BLACK)
692         rbtree_delete_fixup(root, child, parent);
693     free(node);
694 }
695 
696 /* 
697  * 删除键值为key的结点
698  *
699  * 参数说明:
700  *     tree 红黑树的根结点
701  *     key 键值
702  */
703 void delete_rbtree(RBRoot *root, Type key)
704 {
705     Node *z, *node; 
706 
707     if ((z = search(root->node, key)) != NULL)
708         rbtree_delete(root, z);
709 }
710 
711 /*
712  * 销毁红黑树
713  */
714 static void rbtree_destroy(RBTree tree)
715 {
716     if (tree==NULL)
717         return ;
718 
719     if (tree->left != NULL)
720         rbtree_destroy(tree->left);
721     if (tree->right != NULL)
722         rbtree_destroy(tree->right);
723 
724     free(tree);
725 }
726 
727 void destroy_rbtree(RBRoot *root)
728 {
729     if (root != NULL)
730         rbtree_destroy(root->node);
731 
732     free(root);
733 }
734 
735 /*
736  * 打印"红黑树"
737  *
738  * tree       -- 红黑树的节点
739  * key        -- 节点的键值 
740  * direction  --  0,表示该节点是根节点;
741  *               -1,表示该节点是它的父结点的左孩子;
742  *                1,表示该节点是它的父结点的右孩子。
743  */
744 static void rbtree_print(RBTree tree, Type key, int direction)
745 {
746     if(tree != NULL)
747     {
748         if(direction==0)    // tree是根节点
749             printf("%2d(B) is root\n", tree->key);
750         else                // tree是分支节点
751             printf("%2d(%s) is %2d's %6s child\n", tree->key, rb_is_red(tree)?"R":"B", key, direction==1?"right" : "left");
752 
753         rbtree_print(tree->left, tree->key, -1);
754         rbtree_print(tree->right,tree->key,  1);
755     }
756 }
757 
758 void print_rbtree(RBRoot *root)
759 {
760     if (root!=NULL && root->node!=NULL)
761         rbtree_print(root->node, root->node->key, 0);
762 }
复制代码

红黑树的测试文件(rbtree_test.c)

复制代码
 1 /**
 2  * C语言实现的红黑树(Red Black Tree)
 3  *
 4  * @author skywang
 5  * @date 2013/11/18
 6  */
 7 
 8 #include <stdio.h>
 9 #include "rbtree.h"
10 
11 #define CHECK_INSERT 0    // "插入"动作的检测开关(0,关闭;1,打开)
12 #define CHECK_DELETE 0    // "删除"动作的检测开关(0,关闭;1,打开)
13 #define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) )
14 
15 void main()
16 {
17     int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
18     int i, ilen=LENGTH(a);
19     RBRoot *root=NULL;
20 
21     root = create_rbtree();
22     printf("== 原始数据: ");
23     for(i=0; i<ilen; i++)
24         printf("%d ", a[i]);
25     printf("\n");
26 
27     for(i=0; i<ilen; i++)
28     {
29         insert_rbtree(root, a[i]);
30 #if CHECK_INSERT
31         printf("== 添加节点: %d\n", a[i]);
32         printf("== 树的详细信息: \n");
33         print_rbtree(root);
34         printf("\n");
35 #endif
36     }
37 
38     printf("== 前序遍历: ");
39     preorder_rbtree(root);
40 
41     printf("\n== 中序遍历: ");
42     inorder_rbtree(root);
43 
44     printf("\n== 后序遍历: ");
45     postorder_rbtree(root);
46     printf("\n");
47 
48     if (rbtree_minimum(root, &i)==0)
49         printf("== 最小值: %d\n", i);
50     if (rbtree_maximum(root, &i)==0)
51         printf("== 最大值: %d\n", i);
52     printf("== 树的详细信息: \n");
53     print_rbtree(root);
54     printf("\n");
55 
56 #if CHECK_DELETE
57     for(i=0; i<ilen; i++)
58     {
59         delete_rbtree(root, a[i]);
60 
61         printf("== 删除节点: %d\n", a[i]);
62         if (root)
63         {
64             printf("== 树的详细信息: \n");
65             print_rbtree(root);
66             printf("\n");
67         }
68     }
69 #endif
70 
71     destroy_rbtree(root);
72 }
复制代码

 

红黑树的C测试程序

前面已经给出了红黑树的测试程序(rbtree_test.c),这里就不再重复说明。下面是测试程序的运行结果:

复制代码
== 原始数据: 10 40 30 60 90 70 20 50 80 
== 前序遍历: 30 10 20 60 40 50 80 70 90 
== 中序遍历: 10 20 30 40 50 60 70 80 90 
== 后序遍历: 20 10 50 40 70 90 80 60 30 
== 最小值: 10
== 最大值: 90
== 树的详细信息: 
30(B) is root
10(B) is 30's   left child
20(R) is 10's  right child
60(R) is 30's  right child
40(B) is 60's   left child
50(R) is 40's  right child
80(B) is 60's  right child
70(R) is 80's   left child
90(R) is 80's  right child
复制代码

 

posted @ 2021-08-28 00:03  张同光  阅读(48)  评论(0编辑  收藏  举报