[Elementary-Algorithms/6]并不复杂的红黑树

标题我是引用作者的 :)
红黑树是一种平衡的二叉搜索树,它的插入操作和普通的二叉搜索树差不多,只不过多了些平衡的代码。红黑树的具体定义和性质什么的,看原文就可以了,作者讲的很清楚。当然也可以网上搜下,一大把。
既然是”二叉搜索树”,那么它的前驱后继、最大最小、三种遍历等这些操作都是一样的,代码可以参照二叉搜索树这节,这里就不贴出来了。这节主要是解释下我学习后自己实现的插入操作的代码,当然,删除操作的代码也应该讲下的,因为删除操作和普通二叉搜索树的也不同,不过删除操作的代码暂未实现,等实现了再加上。


贴代码之前,我先把原文中作者讲的比较关键的地方贴出来,插入操作根据关键的内容就可以实现了,很简单。
修复代码就是将这四种情况描述清楚就行了,很简单吧,所以看懂这图很重要。截图来自原文。
这里写图片描述
除了上面那张图,看下下面这张图也很有用,因为插入操作需要实现的几个函数和这差不多。截图同样来自原文。
这里写图片描述
图中几个等式的定义的意思就是:要实现某个函数的功能,嵌套另一个函数的功能来实现就行了,可能还会分几种情况,整体就是这样。然后还有作者提到的模式匹配,这个理解为结合率就差不多了。我简单解释下balance(T)的意思:如果树的结构满足match(T)这种情况,我们就把它修复为(R, (B, A, x, B), y, (B, C, z, D))这种结构。这个结构是什么意思呢?就是节点y的左右两个子节点分别是x、z,且节点y的颜色是红色R,然后节点x的左右两个子节点分别是A、B,且节点x的颜色为黑色B,然后节点y的左右两个子节点分别是C、D,且节点y的颜色为黑色B(是不是和结合律差不多呢),如果是其它情况otherwise,就不用修复了,直接是原来的T就行了。其它的等式也这样理解就行了。


下面正式贴代码,我感觉代码有点啰嗦,不知是我实现的啰嗦还是本来就这么啰嗦…肯定有更好地实现代码,我的仅供参考。C语言并没有模式匹配这个功能,所以用模式匹配是不可能的了,只能老老实实写代码进行判断这样子。

红黑树头文件:

#ifndef _RBTREE_H_
#define _RBTREE_H_

// 节点还是只能存unsigned long类型的变量
typedef struct _wtlRBT_NODE wtlRBT_NODE;

typedef struct _wtlRBTREE {
    wtlRBT_NODE* root;
    int height;
} wtlRBTREE;

wtlRBTREE* wtlRBTree_Create(void);

void wtlRBTree_Destroy(wtlRBTREE** tree);

void wtlRBTree_Insert(
    wtlRBTREE* tree, unsigned long value
);

void wtlRBTree_PreOrder(wtlRBTREE* tree);

void wtlRBTree_InOrder(wtlRBTREE* tree);

void wtlRBTree_PostOrder(wtlRBTREE* tree);

wtlRBT_NODE* wtlRBTree_LookUp(
    wtlRBTREE* tree, unsigned long value
);

wtlRBT_NODE* wtlRBTree_Min(wtlRBTREE* tree);

wtlRBT_NODE* wtlRBTree_Max(wtlRBTREE* tree);

wtlRBT_NODE* wtlRBTree_Precursor(
    wtlRBTREE* tree, unsigned long value
);

wtlRBT_NODE* wtlRBTree_Successor(
    wtlRBTREE* tree, unsigned long value
);

/* Functions of node's options */
unsigned long wtlRBTNode_Value(wtlRBT_NODE* node);

wtlRBT_NODE* wtlRBTNode_Left(wtlRBT_NODE* node);

wtlRBT_NODE* wtlRBTNode_Right(wtlRBT_NODE* node);

#endif /* _RBTREE_H_  */

红黑树实现文件:

typedef enum {
    eRED,
    eBLACK
} wtlCOLOR;

typedef struct _wtlRBT_NODE{
    struct _wtlRBT_NODE* parent;
    struct _wtlRBT_NODE* left;
    struct _wtlRBT_NODE* right;
    wtlCOLOR color;
    unsigned long value;
} wtlRBT_NODE;

// 其它函数就不贴出来了,不然太长了
......

// 需要修复的情况1,对应截图中的一种。
// 这函数没什么好讲的,就是代码写的那样,-1表示符合该情况,0表示不符合
int wtlRBTree_CaseA(wtlRBT_NODE* x){
    // 如果给定的节点x为空,或它的颜色为黑色,直接就不符合情况了
    if (NULL == x || wtlRBTNode_Color(x) == eBLACK) {
        return -1;
    }

    // 否则继续判断父节点y的情况
    wtlRBT_NODE* y = wtlRBTNode_Parent(x);
    if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
        return -1;
    }

    // 再判断y的父节点z的情况
    wtlRBT_NODE* z = wtlRBTNode_Parent(y);
    if (x == wtlRBTNode_Left(y) && y == wtlRBTNode_Left(z)) {
        return 0;
    }

    return -1;
}

// 情况1的修复函数。tree就是整棵红黑树,x就是当前插入,且符合情况1的节点
// 注意该函数的调用需要先调用wtlRBTree_CaseA()进行判断,直接调用可能出错的
wtlRBTREE* wtlRBTree_BalanceA(wtlRBTREE* tree, wtlRBT_NODE* x){
    printf("BalanceA\n");

    // 先获取截图中所涉及到的节点
    wtlRBT_NODE* y = wtlRBTNode_Parent(x);
    wtlRBT_NODE* z = wtlRBTNode_Parent(y);
    wtlRBT_NODE* r = wtlRBTNode_Parent(z);
    wtlRBT_NODE* c = wtlRBTNode_Right(y);

    // 直接设置修复后各节点之间的关系,就是截图中中间那种结构。这几个函数没贴出来,不过都是很简单的
    wtlRBTNode_SetRight(y, z);
    wtlRBTNode_SetLeft(z, c);
    wtlRBTNode_SetColor(y, eRED);
    wtlRBTNode_SetColor(x, eBLACK);
    wtlRBTNode_SetColor(z, eBLACK);

    // 这几个节点并不是整棵树,所以需要判断一下
    // 如果节点r为空
    if (NULL == r) {
        // 设置节点y的父节点
        wtlRBTNode_SetParent(y, NULL);
        // 此时整棵树的根节点就是y了
        tree->root = y;
        // 然后返回整棵树
        return tree;
    }

    // 如果原先的结构(未修复之前),节点z为节点r的左节点
    if (z == wtlRBTNode_Left(r)) {
        // 设置节点y为节点r的左节点(修复后)
        wtlRBTNode_SetLeft(r, y);
    } else {
        // 反之设置节点y为节点r的右节点(修复后)
        wtlRBTNode_SetRight(r, y);
    }

    // 返回整棵树
    return tree;
}

// 剩下3中情况和修复函数就懒得讲了,理解情况1,在结合截图来就行了
int wtlRBTree_CaseB(wtlRBT_NODE* z){
    if (NULL == z || wtlRBTNode_Color(z) == eBLACK) {
        return -1;
    }

    wtlRBT_NODE* y = wtlRBTNode_Parent(z);
    if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
        return -1;
    }

    wtlRBT_NODE* x = wtlRBTNode_Parent(y);
    if (z == wtlRBTNode_Right(y) && y == wtlRBTNode_Right(x)) {
        return 0;
    }

    return -1;
}

wtlRBTREE* wtlRBTree_BalanceB(wtlRBTREE* tree, wtlRBT_NODE* z){
    printf("BalanceB\n");

    wtlRBT_NODE* y = wtlRBTNode_Parent(z);
    wtlRBT_NODE* x = wtlRBTNode_Parent(y);
    wtlRBT_NODE* b = wtlRBTNode_Left(y);
    wtlRBT_NODE* r = wtlRBTNode_Parent(x);

    wtlRBTNode_SetLeft(y, x);
    wtlRBTNode_SetRight(x, b);
    wtlRBTNode_SetColor(y, eRED);
    wtlRBTNode_SetColor(x, eBLACK);
    wtlRBTNode_SetColor(z, eBLACK);

    if (NULL == r) {
        wtlRBTNode_SetParent(y, NULL);
        tree->root = y;
        return tree;
    }

    if (x == wtlRBTNode_Left(r)) {
        wtlRBTNode_SetLeft(r, y);
    } else {
        wtlRBTNode_SetRight(r, y);
    }

    return tree;
}

int wtlRBTree_CaseC(wtlRBT_NODE* y){
    if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
        return -1;
    }

    wtlRBT_NODE* x = wtlRBTNode_Parent(y);
    if (NULL == x || wtlRBTNode_Color(x) == eBLACK) {
        return -1;
    }

    wtlRBT_NODE* z = wtlRBTNode_Parent(x);
    if (y == wtlRBTNode_Right(x) && x == wtlRBTNode_Left(z)) {
        return 0;
    }

    return -1;
}

wtlRBTREE* wtlRBTree_BalanceC(wtlRBTREE* tree, wtlRBT_NODE* y){
    printf("BalanceC\n");

    wtlRBT_NODE* x = wtlRBTNode_Parent(y);
    wtlRBT_NODE* z = wtlRBTNode_Parent(x);
    wtlRBT_NODE* b = wtlRBTNode_Left(y);
    wtlRBT_NODE* c = wtlRBTNode_Right(y);
    wtlRBT_NODE* r = wtlRBTNode_Parent(z);

    wtlRBTNode_SetChildren(y, x, z);
    wtlRBTNode_SetRight(x, b);
    wtlRBTNode_SetLeft(z, c);
    wtlRBTNode_SetColor(y, eRED);
    wtlRBTNode_SetColor(x, eBLACK);
    wtlRBTNode_SetColor(z, eBLACK);

    if (NULL == r) {
        wtlRBTNode_SetParent(y, NULL);
        tree->root = y;
        return tree;
    }

    if (z == wtlRBTNode_Left(r)) {
        wtlRBTNode_SetLeft(r, y);
    } else {
        wtlRBTNode_SetRight(r, y);
    }

    return tree;
}

int wtlRBTree_CaseD(wtlRBT_NODE* y){
    if (NULL == y || wtlRBTNode_Color(y) == eBLACK) {
        return -1;
    }

    wtlRBT_NODE* z = wtlRBTNode_Parent(y);
    if (NULL == z || wtlRBTNode_Color(z) == eBLACK) {
        return -1;
    }

    wtlRBT_NODE* x = wtlRBTNode_Parent(z);
    if (y == wtlRBTNode_Left(z) && z == wtlRBTNode_Right(x)) {
        return 0;
    }

    return -1;
}

wtlRBTREE* wtlRBTree_BalanceD(wtlRBTREE* tree, wtlRBT_NODE* y){
    printf("BalanceD\n");

    wtlRBT_NODE* z = wtlRBTNode_Parent(y);
    wtlRBT_NODE* x = wtlRBTNode_Parent(z);
    wtlRBT_NODE* b = wtlRBTNode_Left(y);
    wtlRBT_NODE* c = wtlRBTNode_Right(y);
    wtlRBT_NODE* r = wtlRBTNode_Parent(x);

    wtlRBTNode_SetChildren(y, x, z);
    wtlRBTNode_SetRight(x, b);
    wtlRBTNode_SetLeft(z, c);
    wtlRBTNode_SetColor(y, eRED);
    wtlRBTNode_SetColor(x, eBLACK);
    wtlRBTNode_SetColor(z, eBLACK);

    if (NULL == r) {
        wtlRBTNode_SetParent(y, NULL);
        tree->root = y;
        return tree;
    }

    if (x == wtlRBTNode_Left(r)) {
        wtlRBTNode_SetLeft(r, y);
    } else {
        wtlRBTNode_SetRight(r, y);
    }

    return tree;
}

// 统一修复的函数。tree为整棵树,node为任意节点
wtlRBTREE* wtlRBTree_Balance(wtlRBTREE* tree, wtlRBT_NODE* node){
    // 如果都不符合那四种情况,就不需要修复了,直接返回整棵树
    if (-1 == wtlRBTree_CaseA(node) &&
        -1 == wtlRBTree_CaseB(node) &&
        -1 == wtlRBTree_CaseC(node) &&
        -1 == wtlRBTree_CaseD(node)
    ) {
        return tree;
    }

    // 符合情况1,修复情况1
    if (wtlRBTree_CaseA(node) == 0) {
        printf("caseA:%ld\n", node->value);
        wtlRBTree_BalanceA(tree, node);
    }
    if (wtlRBTree_CaseB(node) == 0) {
        printf("caseB:%ld\n", node->value);
        wtlRBTree_BalanceB(tree, node);
    }
    if (wtlRBTree_CaseC(node) == 0) {
        printf("caseC:%ld\n", node->value);
        wtlRBTree_BalanceC(tree, node);
    }
    if (wtlRBTree_CaseD(node) == 0) {
        printf("caseD:%ld\n", node->value);
        wtlRBTree_BalanceD(tree, node);
    }

    // 修复操作并不是进行一次就行了的,需要递归地修复,直到不存在连续的两个红色节点
    // 如果当前需要修复的节点node的颜色为红色(注意,node可能为父节点,也可能为子节点,
    // 当它为父节点的时候,会被上面的修复操作设置为红色,为子节点则设置为黑色)
    if (wtlRBTNode_Color(node) == eRED) {
        // 此时需要再次对node进行递归地判断修复
        return wtlRBTree_Balance(tree, node);
    } else {
        // 反之,对node的父节点进行递归地判断修复
        return wtlRBTree_Balance(tree, wtlRBTNode_Parent(node));
    }
}

// 设置根节点为黑色。因为每次插入一个节点,都是将其设置为红色的,而树的根节点必须为黑色
static void wtlRBTree_MakeBlack(wtlRBTREE* tree){
    wtlRBTNode_SetColor(tree->root, eBLACK);
}

// 该函数的原理和二叉搜索树中介绍的是一样的,这里就不多讲了,可以点开头给的连接看下
// 注意,因为可能要改变root指向的一级指针的地址,所以要传个二级指针
static void wtlRBTree_InsertRaw(wtlRBT_NODE** root, wtlRBT_NODE* node){
    if (NULL == *root) {
            *root = node;
            printf("insert:%ld\n", (*root)->value);
            return;
        } else {
            node->parent = *root;
        }
        if ((*root)->value == node->value) {
            return;
        }

        if ((*root)->value > node->value) {
            wtlRBTree_InsertRaw(&((*root)->left), node);
        } else {
            wtlRBTree_InsertRaw(&((*root)->right), node);
        }
}

// 红黑树完整的插入函数。tree为整棵树结构,value为要插入的值(因为节点只能存unsigned long类型的值...)
void wtlRBTree_Insert(wtlRBTREE* tree, unsigned long value){
    // 创建一个节点用来存value
    wtlRBT_NODE* node = wtlRBTNode_Create(value);

    // 先按二叉搜索树的插入步骤将节点插入到树中
    wtlRBTree_InsertRaw(&(tree->root), node);
    // 然后再平衡,最后将根节点置黑
    wtlRBTree_MakeBlack(wtlRBTree_Balance(tree, node));
}

简单吧。删除操作后面再加上 :)

猜你喜欢

转载自blog.csdn.net/alonwol/article/details/80636226