Bootstrap

红黑树纯c语言实现--继上一篇单黑色节点删除

一下是红黑树增删查,包含了一个完整的demo。

1. 由于插入操作比较简单,就不讲了直接上代码:

/**
 * @brief Left rotate.
 *   step:
 *      1. right child becomes the parent node, if current node`s parrent is null,
 *          the right node becomes root node.
 *      2. right child`s left child becomes current node`s right node.
 *      3. current node becomes right child`s left child.
 *      current node = previous parent node.
 *  eg:left node a.
 *           a                                c   
 *         /   \     left rotate            /   \
 *        b     c   ===============>>      a     g  
 *      /   \   / \                       / \
 *     d     e  f  g                     b   f
 *                                      / \
 *                                     d   e
 *  @return rd_node_t* 
 *                                    
 * @anchor cj
 */
void left_rotate(rd_node_t **root, rd_node_t *cur_node)
{
    rd_node_t *right_node = cur_node->right;

    /* step 1 */
    right_node->parent = cur_node->parent;
    if (!cur_node->parent) {
        /* current node is root. */
        *root =  right_node;
    } else if (cur_node == cur_node->parent->left) {
        /* if current node is in left tree. */
        cur_node->parent->left = right_node;
    } else {
        /* current node is right tree. */
        cur_node->parent->right = right_node;
    }

    /* step 2 */
    cur_node->right = right_node->left;
    if (right_node->left) {
        right_node->left->parent = cur_node;
    }

    /* step 3 */
    right_node->left = cur_node;
    cur_node->parent = right_node;
}



/**
 * @brief right rotate.
 *  step:
 *      1. left child becomes the parent node.if current node`s parrent is null,
 *          the left node becomes root node.
 *      2. left child`s right child becomes current node`s right node.
 *      3. current node becomes right child`s left child.
 *      current node = previous parent node.
 *  eg:right node a.
 *           a                                b   
 *         /   \     right rotate           /   \
 *        b     c   ===============>>      d     a  
 *      /   \   / \                             /  \
 *     d     e  f  g                           e    c
 *                                                 /  \
 *                                                f    g
 *  @return rd_node_t* 
 *                                    
 * @anchor cj
 */

void right_rotate(rd_node_t **root, rd_node_t *cur_node)
{
    rd_node_t *left_node = cur_node->left;
    struct test_info *info = NULL;

    /* step 1 */
    left_node->parent = cur_node->parent;
    if (!cur_node->parent) {
        /* root node. */
        *root = left_node;
    } else if (cur_node == cur_node->parent->left) {
        /* current node is left tree. */
        cur_node->parent->left = left_node;
    } else {
        /* current node is right tree. */
        cur_node->parent->right = left_node;
    }

    /* step 2. */
    cur_node->left = left_node->right;
    if (left_node->right) {
        left_node->right->parent = cur_node;
    }

    /* step 3. */
    cur_node->parent = left_node;
    left_node->right = cur_node;
}

/**
 * @brief 红黑树性质
 * 1. 每个节点要么是红色,要么是黑色。
 * 2. 根节点是黑色。
 * 3. 每个叶子节点(NIL节点)是黑色。----其实就是空指针叶子
 * 4. 如果一个节点是红色,那么它的两个子节点都是黑色。 ----- 插入一个节点要默认成红色原因,NIL节点是黑色的。
 * 5. 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点。
 * 
 */

/**
 * @brief fix red black tree 
 *  step:
 *  插入修正:
 *    当向红黑树中插入一个节点时,会破坏红黑树的平衡性质。为了修正这种破坏,需要进行以下操作:
 *
 *    a) 如果插入的是根节点,直接将其染成黑色。
 *    b) 如果插入的节点的父节点是黑色的,不需要做任何修正。
 *    c) 如果插入的节点的父节点是红色的,需要进行下面的修正操作:
 *      case 1. 当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色
 *         (01)将“父节点”设为黑色。(02)将“叔叔节点”设为黑色。(03)将“祖父节点”设为“红色” (04)将“祖节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作.    
 *      case 2. 父亲节点是左子树,当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子
 *          (01)将“父节点”作为“新的当前节点”  (02)以“新的当前节点”为支点进行左旋。
 *      case 3. 父亲节点是左子树,当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左孩子
 *          (01)将“父节点”设为“黑色 (02)将“祖父节点”设为“红色”。(03)以“祖父节点”为支点进行右旋。
 *      case 4. 父亲节点是右子树,叔叔节点为黑色或者不存在,当前节点是父节点的左子节点,将设置当前节点为父节点,并以更新后的节点为支点进行右旋。
 *      case 5. 父亲节点是右子树,叔叔节点为黑色或者不存在,当前节点是父节点的右子节点,将当前节点的父节点染黑,祖父节点染红,设置当前节点为祖父节点,并以更新后的当前节点为支点进行左旋。
 * @author cj.2023-12-28
 * 
 * @return NULL
 */
void rd_fix_insert(rd_node_t **root, rd_node_t *cur_node)
{
    rd_node_t *uncle;

    /* 当前是根节点,直接染黑;如果父节点是红色,需要调整;由于根节点始终是黑色的,所以如果父节点是根节点时就不需要遍历调整了 */
    while (cur_node && cur_node->parent && cur_node->parent->parent && (cur_node->parent->color == RD_RED)) {
        if (cur_node->parent->parent->left == cur_node->parent) { /* 父节点是左子树,叔叔节点是右子树 */
            uncle = cur_node->parent->parent->right;
        } else { /* 父节点是右子树,叔叔节点是左子树 */
            uncle = cur_node->parent->parent->left;
        }

        /* 如果叔叔节点存在,则查看叔叔节点是否为红色,如果为红色,则叔叔节点和父亲节点染黑,祖父节点染红,并将当前节点置为祖父节点继续判断 */
        if (uncle && uncle->color == RD_RED) {
            uncle->color = RD_BLACK;
            cur_node->parent->color = RD_BLACK;
            cur_node->parent->parent->color = RD_RED;
            cur_node = cur_node->parent->parent;
            continue;
        } else {
            /* 如果父亲节点是左子树 */
            if (cur_node->parent->parent->left == cur_node->parent) {
                /* 如果叔叔节点不存在或者是黑色,且插入节点为左子节点,则将父节点染黑,祖父节点染红,并以祖父节点为支点进行右旋 */
                if (cur_node->parent->left == cur_node) {
                    cur_node->parent->color = RD_BLACK;
                    cur_node->parent->parent->color = RD_RED;
                    cur_node = cur_node->parent->parent;
                    right_rotate(root, cur_node);
                } else {
                    /* 如果插入节点是右子树,并以父节作为当前节点,以父节点为支点左旋 */
                    cur_node = cur_node->parent;
                    left_rotate(root, cur_node);
                }
            } else { /* 如果父亲节点是右子树 */
                /* 如果叔叔节点不存在或者是黑色,且插入节点为右子节点,则将父节点染黑,祖父节点染红,并以祖父节点为支点进行右旋 */
                if (cur_node->parent->right == cur_node) {
                    cur_node->parent->color = RD_BLACK;
                    cur_node->parent->parent->color = RD_RED;
                    cur_node = cur_node->parent->parent;
                    left_rotate(root, cur_node);
                } else {
                    /* 如果插入节点是左子树,并以父节作为当前节点,以父节点为支点右旋 */
                    cur_node = cur_node->parent;
                    right_rotate(root, cur_node);
                }
            }
        }
    }

    (*root)->color = RD_BLACK;
}

/**
 * @brief insert rd node.
 *     
 * 
 * @author cj.2023-12-28
 * 
 * @return NULL
 * 
 */

void rd_insert_node(rd_node_t **root, rd_node_t *new_node)
{
    rd_node_t *cur_node = *root;

    if (!(*root)) {
        *root = new_node;
        return;
    }

    if (!new_node) {
        return;
    }

    while (cur_node) {
        if (cur_node->index < new_node->index) {
            if (cur_node->right) {
                cur_node = cur_node->right;
            } else {
                cur_node->right = new_node;
                new_node->parent = cur_node;
                rd_fix_insert(root, new_node);
                break;
            }
        } else {
            if (cur_node->left) {
                cur_node = cur_node->left;
            } else {
                cur_node->left = new_node;
                new_node->parent = cur_node;
                rd_fix_insert(root, new_node);
                break;
            }
        }
    }

}

2. 第二部分是查找一个节点

rd_node_t *find_rd_node(rd_node_t *root, int index)
{
    rd_node_t *cur_node = root;

    while (cur_node) {
        if (index > cur_node->index) {
            cur_node = cur_node->right;
        } else if (index < cur_node->index) {
            cur_node = cur_node->left;
        } else {
            return cur_node;
        }
    }

    return NULL;
}

3. 删除比较复杂,建议新手先阅读一下上一篇删除单个节点情况

/**
 * @brief delete a rd node.
 *      1. 删掉一个单独的红色节点,该红节点无孩子,直接删除即可。
 *      2. 被删节点node为黑色且无子节点
 *           case 1: 兄弟节点sibling有一个与sibling方向一致的红色子节点【方向一致是指节点与子节点都是左子节点或者都是右子节点】,
 *              如果删除节点node是左节点,兄弟节点为父节点颜色, 父节点黑色,兄弟节点孩子黑色,以父节点为支点左旋,否则右旋。
 *           case 2:兄弟节点sibling为黑色,且有一个与sibling方向相反的红色子节点,删除node,将sibling与sibling的son旋转,然后父节点和兄弟孩子的颜色对调,
 *              父节点置为黑色,转换成【case 1】
 *           case 3:兄弟节点sibling为黑色且无红色子节点,直接删除node,并进行以下操作:
 *              1). 若父节点为红色,则父亲节点颜色和兄弟节点颜色对调。结束。
 *              2). 若父节点为黑色,则将node置为father节点继续向上判断,然后继续判断直到到达根为止。
 *           case 4:如果兄弟是红色,则以父节点为支点进行旋转,从而变成case 1、2、3的一种情况
 *      3. 被删节点是红色的且只有一个子节点,这种是异常情况。---抛出异常
 *      4. 被删节点只有一个子节点且被删节点为黑色,直接用子节点替代被删节点,然后染黑即可。
 *      5. 被删节点有两个子节点
 *            node         
 *          /      \
 *      left        right
 *     /   \         /    \
 *       前驱节点  后继节点   
 *     
 *     注:前驱节点:左子树上最大值,后继节点右子树上的最小值。
 *     使用前驱节点或者后继节点的值替代node,不需要改变颜色,然后删掉前驱节点或者后继节点。
 *        (1)如果前驱或者后继是红色,且无孩子退化成【组合1】,黑色无孩子退化成【组合2】
 *        (2)如果前驱或者后继是红色,且有一个孩子,则退化成【组合1】或者【组合3】,否则黑色有一个孩子退化成【组合4】    
 * @author cj.2023-1-2
 * 
 * @return
 *      if exist the node, return aim rd node.
 *      else return NULL;
 * 
 */

/* 查找前驱节点 */
static rd_node_t *find_precursor(rd_node_t *cur_node)
{
    while (cur_node) {
        if (cur_node->right) {
            cur_node = cur_node->right;
            continue;
        }

        break;
    }

    return cur_node;
}

/* 查找后继节点 */
static rd_node_t *find_successor(rd_node_t *cur_node)
{
    while (cur_node) {
        if (cur_node->left) {
            cur_node = cur_node->left;
            continue;
        }

        break;
    }

    return cur_node;
}

void dump_rd_tree(rd_node_t *root)
{
    if (!root) {
        return;
    }

    printf("val = %d color=%d", root->index, root->color);
    if (root->parent) {
        printf("parent index=%d\n", root->parent->index); 
    } else {
        printf("\n");
    }
    dump_rd_tree(root->left);
    dump_rd_tree(root->right);
}

/**
 * @brief delete a black node without child.
 * case 1:兄弟为黑色,且有红孩子:
 *      (1)如果兄弟节点和其子节点方向一致,则子节点=兄弟节点颜色,兄弟节点=父节点颜色,父节点=被删节点颜色,然后旋转(删除左孩子就左旋,否则右旋)
 *      (2)如果兄弟节点和其子节点方向一致,则子节点=父节点颜色,父节点=被删节点颜色,然后以兄弟为支点旋转(删除左孩子右旋,否则左旋),转为(1)
 * 
 * case 2: 兄弟为黑色,此时兄弟一定没有孩子
 *      父亲为红色:则兄弟改为红色,父亲改成黑色。结束
 *      父亲为黑色: 将兄弟染红,以父亲为节点继续向上遍历,期望遇到其他情况化解少一个黑色节点的情况。或者一直遇不到其他情况,每层子树少一个黑色节点,
 *              这样直到根节点时让根的另一个子树也少一个黑色节点,根两边平衡,结束。
 * 
 * case 3: 兄弟为红色,则一定有两个黑色孩子,且父亲必定是黑色,否则不满足性质5.
 *      将父亲节点染红、兄弟节点染黑,然后以父亲节点旋转(删掉节点是左子树就左旋,否则右旋),转换成其他的case。
 * 
 * @param root : rd tree root noder.
 * @param del_node : The rd node to be delete.
 * @return balance ok: 0      ocurr error: -1
 */
char rd_tree_balance_single_black_node(rd_node_t **root, rd_node_t *del_node)
{
    rd_node_t *p_del = del_node;
    rd_node_t *sibling = NULL;
    rd_node_t *sibling_child = NULL;
    u8 sibling_direct, sibling_child_direct;

    while (p_del && !rd_is_root(p_del, *root)) {
        if (p_del->parent->left == p_del) {
            sibling_direct = RD_CHILD_RIGHT;
        } else {
            sibling_direct = RD_CHILD_LEFT;
        }

        sibling = rd_child(p_del->parent, sibling_direct);
        if (sibling) {
            /* case 1 */
            if ((sibling->left && (sibling->left->color == RD_RED)) ||
                (sibling->right && (sibling->right->color == RD_RED))) { /* sibling have RED child */
                    if ((sibling_child = rd_child(sibling, sibling_direct)) 
                    && (sibling_child->color == RD_RED)) {
                    /* The direction of child is consistent with that of the sibling. */
                    sibling_child->color = RD_BLACK;
                    sibling->color = sibling->parent->color;
                    sibling->parent->color = RD_BLACK;
                    if (sibling_direct == RD_CHILD_LEFT) {
                        right_rotate(root, sibling->parent);
                    } else {
                        left_rotate(root, sibling->parent);
                    }
                } else { /* The direction of child is opposite to that of the sibling. */
                    sibling_child_direct = rd_opposite_direct(sibling_direct);
                    sibling_child = rd_child(sibling, sibling_child_direct);
                    sibling_child->color = sibling->parent->color;
                    sibling->parent->color = RD_BLACK;
                    if (sibling_direct == RD_CHILD_LEFT) {
                        left_rotate(root, sibling);
                        right_rotate(root, sibling_child->parent); /* notification: now sibling_child is sibling */
                    } else {
                        right_rotate(root, sibling);
                        left_rotate(root, sibling_child->parent); /* notification: now sibling_child is sibling */
                    }
                }
                break;
            } else {
                /* case 2: sibling is black, father is black or red. */
                if (rd_color_is_black(sibling)) {
                    if (rd_color_is_black(sibling->parent)) {
                        sibling->color = RD_RED;
                        p_del = sibling->parent;
                        continue;        
                    } else {
                        sibling->color = RD_RED;
                        sibling->parent->color = RD_BLACK;
                        break;  
                    }
                } else if (rd_color_is_black(sibling->parent) && !rd_color_is_black(sibling)) {
                    /* case 3: father black and sibling red. */
                    sibling->color = RD_BLACK;
                    sibling->parent->color = RD_RED;
                    if (sibling_direct == RD_CHILD_LEFT) {
                        right_rotate(root, sibling->parent);
                    } else {
                        left_rotate(root, sibling->parent);
                    }
                } else {
                    dump_rd_tree(*root);
                    rd_debug(RD_DEBUG_LEVEL_ERR, rd_debug_info[RD_DEBUG_PROPERTY_5_ERR]);
                    return RET_ERR;
                }
            }
        } else {
            dump_rd_tree(*root);
            rd_debug(RD_DEBUG_LEVEL_ERR, rd_debug_info[RD_DEBUG_PROPERTY_5_ERR]);
            return RET_ERR;            
        }
    }

    return RET_OK;
}


/**
 * @brief swap rd node position.
 * @param root : rd tree root noder.
 * @param s1 : swap node.
 * @param s2 : swap node 2.
 * @return balance ok: 0      ocurr error: -1
 * 
 */
char swap_node(rd_node_t **root, rd_node_t *s1, rd_node_t *s2)
{
    rd_node_t *d_node = s1, *s_node= s2;
    rd_node_t tmp = {0};
    rd_node_t *rd_tmp_ptr;
    u8 direct = RD_CHILD_LEFT;

    if (!d_node || !s_node) {
        rd_debug(RD_DEBUG_LEVEL_ERR, "src node or dest node is null.");
        return RET_ERR;
    }

restart:
    if (s_node->left == d_node) {
        direct = RD_CHILD_LEFT;
    } else if (s_node->right == d_node) {
        direct = RD_CHILD_RIGHT;
    } else if (s_node->parent == d_node) {
        rd_tmp_ptr = d_node;
        d_node = s_node;
        s_node = rd_tmp_ptr;
        goto restart;
    } else {
        direct = RD_CHILD_NONE;
    }

    memcpy(&tmp, s_node, sizeof(rd_node_t));
    /* swap color */
    s_node->color = d_node->color;
    d_node->color = tmp.color;
    /* s_node swap d_node */
    s_node->left = d_node->left;
    if (d_node->left) {
        d_node->left->parent = s_node;
    }

    s_node->right = d_node->right;
    if (d_node->right) {
        d_node->right->parent = s_node;
    }

    /* d_node swap s_node */
    rd_tmp_ptr = d_node->parent;
    d_node->parent = s_node->parent;
    if (s_node->parent) {
        if (s_node->parent->left == s_node) {
            s_node->parent->left = d_node;
        } else {
            s_node->parent->right = d_node;
        }           
    }

    if (direct == RD_CHILD_NONE) {
        s_node->parent = rd_tmp_ptr;
        if (s_node->parent) {
            if (s_node->parent->left == d_node) {
                s_node->parent->left = s_node;
            } else {
                s_node->parent->right = s_node;
            }
        }
        d_node->left = tmp.left;
        if (d_node->left) {
            d_node->left->parent = d_node;
        }
        d_node->right = tmp.right;
        if (d_node->right) {
            d_node->right->parent = d_node;
        }        
    } else {
        s_node->parent = d_node;
        *rd_child_ptr(d_node, direct) = s_node;
        direct = rd_opposite_direct(direct);
        *rd_child_ptr(d_node, direct) = rd_child(&tmp, direct);
        if (rd_child(d_node, direct)) {
            (*rd_child_ptr(d_node, direct))->parent = d_node;
        }
    }

    if (!s_node->parent) {
        *root = s_node;
    }

    if (!d_node->parent) {
        *root = d_node;
    }  

    return RET_OK;
}

char rd_delete_node_balance_tree(rd_node_t **root, rd_node_t *del_node)
{
    rd_node_t *p_del = del_node;
    rd_node_t *rd_tmp;

    if (!p_del) {
        rd_debug(RD_DEBUG_LEVEL_ERR, "The delete node is null.");
        return RET_ERR;
    }

    // dump_rd_tree(*root);
    while (p_del) {
        if (rd_empty_child(p_del)) { /* the node don`t have child. */
            if (rd_color_is_black(p_del)) { /* black need balance, red just delete. */
                return rd_tree_balance_single_black_node(root, p_del);
            }
            break;
        } else if (rd_two_child(p_del)) { /* the node have two children. */
            rd_tmp = find_precursor(p_del->left);
            if (swap_node(root, rd_tmp, p_del) == RET_ERR) {
                rd_debug(RD_DEBUG_LEVEL_ERR, "presursor swap delete node failed!");
                return RET_ERR;
            }
            continue;
        } else { /* the node just have a child. */
            /* just have a child, must be black node, and it`s child is red. */
            if (rd_color_is_black(p_del)) {
                if (!(rd_tmp = rd_child(p_del, RD_CHILD_LEFT))) {
                    rd_tmp = rd_child(p_del, RD_CHILD_RIGHT);
                }

                if (swap_node(root, rd_tmp, p_del) == RET_ERR) {
                    rd_debug(RD_DEBUG_LEVEL_ERR, "presursor swap delete node failed!");
                    return RET_ERR;
                }
                continue;
            } else {
                dump_rd_tree(*root);
                rd_debug(RD_DEBUG_LEVEL_ERR, rd_debug_info[RD_DEBUG_PROPERTY_5_ERR]);
                return RET_ERR;
            }
        }       
    }

    return RET_OK;
}

char rd_remove_node(rd_node_t *del_node)
{
    if (!rd_empty_child(del_node)) {
        rd_debug(RD_DEBUG_LEVEL_ERR, "delete node has child, delete failed!");
        return RET_ERR;
    }

    if (del_node) {
        if (del_node->parent) {
            if (del_node->parent->left == del_node) {
                del_node->parent->left = NULL;
            } else {
                del_node->parent->right = NULL;
            }
        }

        del_node->left = NULL;
        del_node->right = NULL;
        del_node->parent = NULL;
    }
}

char rd_delete_node(rd_node_t **root, rd_node_t *del_node)
{
    if (!del_node) {
        rd_debug(RD_DEBUG_LEVEL_WARNING, "delete rd node is null!");
        return RET_ERR;
    }

    if (rd_delete_node_balance_tree(root, del_node)) {
        rd_debug(RD_DEBUG_LEVEL_ERR, "balance rd tree before delete a node error!");
        return RET_ERR;
    }

    return rd_remove_node(del_node);
}

4. rd_tree.h 和 common.h

//rd_tree.h
#ifndef _RD_TREE_H
#define _RD_TREE_H

#include "common.h"

/********************************************************************************************************
 *                                  enum 
********************************************************************************************************/
enum {
    RD_CHILD_LEFT = 0,
    RD_CHILD_RIGHT,
    RD_CHILD_NONE
};

enum {
    RD_DEBUG_PROPERTY_1_ERR = 0,
    RD_DEBUG_PROPERTY_2_ERR,
    RD_DEBUG_PROPERTY_3_ERR,
    RD_DEBUG_PROPERTY_4_ERR,
    RD_DEBUG_PROPERTY_5_ERR
};

enum {
    RD_DEBUG_LEVEL_PRINT = 0,
    RD_DEBUG_LEVEL_INFO,
    RD_DEBUG_LEVEL_DEBUG,
    RD_DEBUG_LEVEL_WARNING,
    RD_DEBUG_LEVEL_ERR
};

/********************************************************************************************************
 *                                  struct 
********************************************************************************************************/
typedef enum {
    RD_RED = 0,
    RD_BLACK,
} rd_color;

typedef struct rd_node {
    int color;
    int index;
    struct rd_node *left;
    struct rd_node *right;
    struct rd_node *parent;
} rd_node_t;

/********************************************************************************************************
 *                                  define
********************************************************************************************************/
#define rd_empty_child(node) (!node->left && !node->right)  /* 没有子节点 */
#define rd_two_child(node) (node->left && node->right)
#define rd_color_is_black(node) (node->color == RD_BLACK)
#define rd_child(ptr, child) (((child) == RD_CHILD_LEFT) ? ((ptr)->left) : ((ptr)->right)) /* 根据值获取子节点 */
#define rd_is_root(ptr, root) ((ptr) == (root))
#define rd_opposite_direct(direct) ((direct == RD_CHILD_LEFT)?RD_CHILD_RIGHT:RD_CHILD_LEFT)

#define debug_level_set RD_DEBUG_LEVEL_DEBUG
#define rd_debug(debug_level, ...) \
{ \
    if (debug_level >=  debug_level_set) { \
        printf("[RD_DBG][%25s][%4d]:", __FUNCTION__, __LINE__); \
        printf(__VA_ARGS__); \
        printf("\n"); \
    } \
}


static inline rd_node_t **rd_child_ptr(rd_node_t *ptr, u8 direct)
{
    if (direct == RD_CHILD_LEFT) {
        return &(ptr->left);
    } else {
        return &(ptr->right);
    }
}

/********************************************************************************************************
 *                                  declare
********************************************************************************************************/

rd_node_t *create_rd_node();
void left_rotate(rd_node_t **root, rd_node_t *cur_node);
void right_rotate(rd_node_t **root, rd_node_t *cur_node);
void rd_fix_insert(rd_node_t **root, rd_node_t *cur_node);
void rd_insert_node(rd_node_t **root, rd_node_t *new_node);
rd_node_t *find_rd_node(rd_node_t *root, int index);
char rd_tree_balance_single_black_node(rd_node_t **root, rd_node_t *del_node);
char swap_node(rd_node_t **root, rd_node_t *s1, rd_node_t *s2);
char rd_remove_node(rd_node_t *del_node);
char rd_delete_node(rd_node_t **root, rd_node_t *del_node);
void dump_rd_tree(rd_node_t *root);
#endif


//common.h

#ifndef _COMMON_H
#define _COMMON_H

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long u64;

typedef unsigned long u64_ptr_t;


#define RET_ERR (-1)
#define RET_OK (0)
#define PARAMTER_ERR (-2)

#endif


5. 一个小demo,大致的思路就是使用双向链表和红黑树插入大量的数据,然后分别对红黑树和双向链表进行查找,对比两者的性能,另外就是使用红黑树删除一些节点,查看结果是否与预期相符。最后释放内存。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "info.h"

struct stack mystack = {0};
dlist_head_t rand_info_head;

/**
 * @brief tree pre trave
 *     recursion trave binary tree.
 * 
 * @return NULL
 *                                    
 * @anchor cj
 */
void pre_trave_recursion(rd_node_t *rd)
{
    struct test_info *info = NULL;

    if (!rd) {
        return;
    }

    info = container_of(rd, struct test_info, rd);
    printf("<%s %d> info=%c index=%d\n", __FUNCTION__, __LINE__, info->info, rd->index);
    pre_trave_recursion(rd->left);
    pre_trave_recursion(rd->right);
}

/**
 * @brief tree middle trave
 *     recursion trave binary tree.
 * 
 * @return NULL
 *                                    
 * @anchor cj
 */
void mid_trave_recursion(rd_node_t *rd)
{
    struct test_info *info = NULL;

    if (!rd) {
        return;
    }

    mid_trave_recursion(rd->left);
    info = container_of(rd, struct test_info, rd);
    printf("<%s %d> info=%c index=%d\n", __FUNCTION__, __LINE__, info->info, rd->index);
    mid_trave_recursion(rd->right);
}

/**
 * @brief tree post trave
 *     recursion trave binary tree.
 * 
 * @return NULL
 *                                    
 * @anchor cj
 */
void post_trave_recursion(rd_node_t *rd)
{
    struct test_info *info = NULL;

    if (!rd) {
        return;
    }

    post_trave_recursion(rd->left);
    post_trave_recursion(rd->right);
    info = container_of(rd, struct test_info, rd);
    printf("<%s %d> info=%c index=%d\n", __FUNCTION__, __LINE__, info->info, rd->index);
}


/**
 * @brief 先序遍历
 * 
 * @param t 
 */
void pre_trave(rd_node_t *rd)
{
    rd_node_t *node = NULL;
    struct test_info *info = NULL;

    if (rd == NULL) {
        return;
    }

    printf("先序遍历...\n");
    mystack.stack_ptr = -1;
    push_entry(&mystack, (long unsigned int)rd);
    while(1) {
        node = (rd_node_t *)pop_entry(&mystack);
        if (node) {
            info = container_of(node, struct test_info, rd);
            printf("<%s %d> info=%c index=%d\n", __FUNCTION__, __LINE__, info->info, info->rd.index);
            if (node->right) {
                if (push_entry(&mystack, (long unsigned int)node->right)) {
                    break;
                }
            }

            if (node->left) {
                if (push_entry(&mystack, (long unsigned int)node->left)) {
                    break;
                }
            }
            continue;
        }

        break;
    }
}

static struct test_info *create_info_node(char c)
{
    struct test_info *info = (struct test_info *)malloc(sizeof(struct test_info));
    memset(info, 0, sizeof(struct test_info));
    struct test_info *test = NULL;
    info->info = c;

    // test = container_of(&info->rd, struct test_info, rd);
    // printf("test->info=%c\n", test->info);
    return info;
}

static void show_list_info(dlist_t *head, rd_node_t *root)
{
    struct test_info *info = NULL;
    int count = 0, list_num = 0, all_count = 0;

    foreach_dlist(info, head, list) {
        count = test_find_rd_node(root, info->rd.index);
        all_count += count;
        list_num++;
        printf("<%s %d> info=%c count=%d\n", __FUNCTION__, __LINE__, info->info, count);
    }

    if (list_num) {
        printf("show_list_info: mean find times %f\n", ((float)all_count)/list_num);
    }
}

static int64 get_current_u_time() {
    struct timespec time;
    int64 elapsed_us = 0;

    clock_gettime(CLOCK_MONOTONIC, &time);

    elapsed_us = time.tv_sec * 1000000LL + time.tv_nsec / 1000LL;

    return elapsed_us;
}

/**
 * @brief build a tree for test red balck tree.
 *      I defined a struct test_info, it include dlist and rd tree so that I can
 *           view all info list throught dlist or rd tree. But my main aim is to 
 *           test rd tree.
 * @return NULL
 *                                    
 * @anchor cj
 * 
 * 
 */
static void build_rd_tree(rd_node_t **root, dlist_head_t *info_list_head)
{
    struct test_info *info = NULL;
    int i, random_num, rand_list_sz = 0, rand_list_max_sz = 1000;
    char c;
    unsigned int rd_create_num = 200000;
    unsigned int rd_num_range = rd_create_num + 100000;
    struct test_info *info_random;
    int64 start, end;

    init_dlist(&rand_info_head.head);

    start = get_current_u_time();
    srand(time(NULL));
    for (i = 0; i < rd_create_num; i++) {
        random_num = rand() % rd_num_range;
        /* 防止index重复 */
        while(find_rd_node(*root, random_num)) {
            random_num = rand() % rd_num_range;
        }

        // printf("random_num=%d\n", random_num);
        c = 'a' + random_num % 52;
        info = create_info_node(c);
        list_add(&info_list_head->head, &info->list);
        info->rd.index = random_num;
#if 1
        rd_insert_node(root, &info->rd);
#endif

#if 1 //test rd cabability
        if (rand_list_sz < rand_list_max_sz) {
            if((random_num % 2) == 0) {
                rand_list_sz++;
                info_random = create_info_node(c);
                info_random->rd.index = random_num;
                // printf("info_random: rd->index=%d\n", info_random->rd.index);
                list_add(&rand_info_head.head, &info_random->list);
            }
        }
#endif
    }

    end = get_current_u_time();
    printf("build_rd_tree: time=%ld(us)\n", (end - start));
}

static void free_info_list(dlist_head_t *info_list_head)
{
    struct test_info *info = NULL;
    if (!info_list_head) {
        return;
    }

    foreach_dlist(info, (&info_list_head->head), list) {
        free(info);
    }
}

static void delete_random_rd_node_by_dlist(rd_node_t **root)
{
    struct test_info *info = NULL;
    rd_node_t *del_node = NULL;

    if (!root || !(*root)) {
        return;
    }

    printf("[%s %d]\n", __FUNCTION__, __LINE__);
    foreach_dlist(info, (&rand_info_head.head), list) {
        // printf("delete:info=%c rd->index=%d\n", info->info, info->rd.index);
        del_node = find_rd_node(*root, info->rd.index);
        if(rd_delete_node(root, del_node)) {
            rd_debug(RD_DEBUG_LEVEL_ERR, "rm rd node failed!");
            break;
        }
    }
}

static void test_random_search_val(dlist_head_t *info_list_head, rd_node_t **root)
{
    struct test_info *info = NULL, *info_search = NULL;
    dlist_t *info_d_list = 0;
    int search_count = 0;
    int num_dlist = 0;
    int64 start, end; 

    if (!info_list_head) {
        return;
    }

    /* 测试双向链表的平均搜索次数和耗时 */
    start = get_current_u_time();
    foreach_dlist(info, (&rand_info_head.head), list) {
        num_dlist++;
        foreach_dlist(info_search, (&info_list_head->head), list) {
            search_count++;
            if (info_search->rd.index == info->rd.index) {
                break;
            }
        }
    }

    end = get_current_u_time();
    printf("dlist search mean=%f take time=%ld(us)\n", ((float)search_count)/num_dlist, (end - start));

#if 1
    search_count = 0;

    /* 测试红黑树的平均搜索次数和耗时 */
    start = get_current_u_time();
    foreach_dlist(info, (&rand_info_head.head), list) {
        num_dlist++;
        search_count += test_find_rd_node(*root, info->rd.index);
    }

    end = get_current_u_time();
    printf("rd tree search mean=%f take time=%ld(us)\n", ((float)search_count)/num_dlist, (end - start));
#endif
}

int main(int argc, char **argv)
{
    rd_node_t *root = NULL;
    dlist_head_t info_list_header;
    // struct test_info *p_info = NULL;

    init_dlist(&info_list_header.head);
    build_rd_tree(&root, &info_list_header);
//    show_list_info(&info_list_header.head, root);

    //test rd_tree search cabablity.
    test_random_search_val(&info_list_header, &root);

    // dump_rd_tree(root);
    delete_random_rd_node_by_dlist(&root);
    // dump_rd_tree(root);
    free_info_list(&info_list_header);
}

总体来说红黑树的构建和删除比较耗时,其效率远远低于链表,而搜索效率远远高于链表,尤其是数据量越大时优势越明显,当数据量达到千万级别时,红黑树由于其稳定的搜索性能带来的好处远大于链表。而数据量只有几十几百个差别并不大。即几十上百个数据、查询不频繁或者遍历时用链表,数据量适中且查询频繁可以考虑使用hash表,大数据级别用红黑树和hash表。

;