红黑树(Red Black Tree)

1. 概述

一棵二叉树如果满足下面的红黑性质,则为一棵红黑树:

1)每个节点或是红的,或是黑的

2)根节点是黑的

3)每个叶节点(NIL)是黑的。

4)如果一个节点是红的,则它的儿子都是黑的

5)对每个节点,从该节点到其子孙节点的所有路径包含相同数目的黑节点
一棵二叉树的形状如下图:


2. 红黑树的高度

1)先证以x为根的子树至少包含(利用红黑树的性质5即可证明)

2)一棵含有n个节点的红黑树的黑高度至少是h / 2,再由1)我们可以得到 ,所以 。

因此可以得出红黑树的操作可以在O(lgn)时间内完成。

3.红黑树的基本数据结构

1)红黑树的叶节点

typedef struct Node{
    Color color;
    struct Node *left;
    struct Node *right;
    struct Node *p;
    ElemType key;
}Node;
2)红黑树

typedef struct Tree{
    Node *nillNode;
    Node * root;
}*RedBlackTree;

4. 红黑树的两个基本操作----左旋和右旋


1)左旋代码参考

void leftRotate(RedBlackTree t, Node *x) {
    // Suppose the right node exists(right node not null)
    Node * y = x->right;
    //turn y's left subtree into x's right subtree
    x->right = y->left;
    if(y->left != t->nillNode) {
        y->left->p = x;
    }
    y->p = x->p;
    if(x->p == t->nillNode) {      // x is the root of the tree
        t->root = y;
    } else if (x == x->p->left) {
        x->p->left = y;
    } else {
        x->p->right = y;
    }

    // put x on y's leftg
    y->left = x;
    // link x->p to y
    x->p = y;
}
2)右旋代码参考
void rightRotate(RedBlackTree t, Node *x) {
    Node *y = x->left;
    x->left = y->right;
    if(y->left != t->nillNode) {
        y->right->p = x;
    }
    y->p = x->p;
    if(x->p == t->nillNode) {
        t->root = y;
    } else if (x == x->p->left) {
        x->p->left = y;
    } else {
        x->p->right = y;
    }
    y->right = x;
    x->p = y;
}

5. 最小值

和搜索二叉树差不多即一直往左遍历

// find the min key with x as the root
Node * min(RedBlackTree t, Node * x) {
    if (x != t->nillNode) {
        Node *y = x->left;
        while (y != t->nillNode) {
            x = y;
            y = x->left;
        }
    }
    return x;
}

6. 最大值

// find the min key with x as the root
Node * min(RedBlackTree t, Node * x) {
    if (x != t->nillNode) {
        Node *y = x->left;
        while (y != t->nillNode) {
            x = y;
            y = x->left;
        }
    }
    return x;
}

7. 后继

Node *successor(RedBlackTree t, Node * x) {
    if(x->right != t->nillNode) {
        return min(t, x->right);
    }
    Node * y = x->p;
    while (y != t->nillNode && x == y->left) {
        x = y;
        y = y->p;
    }
    return y;
}

8. 构建一棵红黑树

// create red black tree and initial
void createTree(RedBlackTree *t) {
    // global nil node
    Node * nillNode = NULL;
    // create nillNode and initial
    nillNode = (Node *)malloc(sizeof(Node));
    if(nillNode == NULL) {
        printf("nilNode allocation failed!\n");
        exit(1);
    }
    nillNode->color = BLACK;
    nillNode->left = NULL;
    nillNode->right = NULL;
    nillNode->p = NULL;

    //create Red Black Tree and initial
    (*t) = (RedBlackTree) malloc(sizeof(struct Tree));
    if(*t == NULL) {
        printf("red black allocation failed!\n");
        exit(-1);
    }
    (*t)->root = nillNode;
    (*t)->nillNode = nillNode;
}

9. 插入

1)首先我们像二叉树那样把节点插入到对应的节点,并设置其为红色节点

2)我们对其进行调整,以维持红黑性质

在整个插入过程和调整过程中,只有可能违反红黑性质的2)和4)即根节点是黑色的以及红色节点的儿子节点是黑色节点。(在插入步骤1)中,很容易得出性质2)和4都可能违反),根据如下的调整过程,我们也很容易得出只有性质2)和性质4)可以被违反。




上述过程转化过程:


转化成伪代码:

    if (case 1) {
        
    } else {
        if (case 2) {
            
        }
        case 3
    }
从case1 到case3我们发现并没有违反性质1)3)5),而违反性质1)说明根是红色的,只要将其变成黑色的就OK,所有的性质都可以得到满足,剩下的所有情况就是违反性质4,当遇到case1时z是不断上升的,只要遇到case2 或case3,整个调整过程即将完成。

代码为:

// used for fix up when insert
void insertFixUp(RedBlackTree t, Node *z) {
    Node * y = t->nillNode;
    while (z->p->color == RED) {
        if(z->p == z->p->p->left) {
            // find z's uncle node
            y = z->p->p->right;
            // case 1
            if (y->color == RED) {
                z->p->color = BLACK;
                y->color = BLACK;
                z = z->p->p;
                z->color = RED;
            } else {    // y->color == Black
                // case 2
                if(z == z->p->right) {
                    z = z->p;
                    leftRotate(t, z);
                }
                // case 3
                z->p->color = BLACK;
                z->p->p->color = RED;
                rightRotate(t, z->p->p);
            }
        } else {    // the same as above with "right"  and "left" exchanged
            y = z->p->p->left;
            //case 1
            if (y->color == RED) {
                z->p->color = BLACK;
                y->color = BLACK;
                z = z->p->p;
                z->color = RED;
            } else {
                // case 2
                if(z == z->p->left) {
                    z = z->p;
                    rightRotate(t, z);
                }
                // case3:
                z->p->color = BLACK;
                z->p->p->color = RED;
                leftRotate(t, z->p->p);
            }
        }
    }
    t->root->color = BLACK;
}

10. 删除

1)按照二叉搜索树进行删除

2)如果删除的是黑色节点,则为了维持红黑性质进行相应的调整

其过程代码如下:

void delete(RedBlackTree t, ElemType key) {
    Node * z = find(t, key);
    if (z != t->nillNode) {
        // the node which will be deleted
        Node *y = t->nillNode;
        if (z->left == t->nillNode || z->right == t->nillNode) {
            y = z;
        } else {
            y = successor(t, z);
        }
        Node *x;
        if (y->left != t->nillNode) {
            x = y->left;
        } else {
            x = y->right;
        }
        x->p = y->p;
        if (y->p == t->nillNode) {
            t->root = x;
        } else {
            if (y == y->p->left) {
                y->p->left = x;
            } else {
                y->p->right = x;
            }
        }
        if (y != z) {
            z->key = y->key;
        }
        free(y);
        if (y->color == BLACK) {
            deleteFixUp(t, x);
        }
    }
}

我们知道如果删除的是红色的节点的话,则红黑性质依旧是可以满足的,若是删除的是黑色则情况稍有复杂。

可能违反如下性质:

违反性质2):如果删除的节点y原先是根节点,而y的一个红色的儿子节点成了根节点,则违反了性质2)

违反性质4):如果删除节点y的儿子节点x 和 其父亲节点p[y]都是红色的,则违反了性质4)

违反性质5):因为删除的节点是黑色的,必然使得经过该节点的路径黑高度少1,则违反性质5)

综上违反的性质,我们删除节点y的儿子节点x当做一个双重颜色节点,即附加一层黑色(若x原先是黑色的,现在有双重黑色属性,若是原先是黑色的则现在具有红和黑的性质),因此在调整过程中,认为性质5)是维持的。由于我们在调整过程中x节点左右子树是满足红黑树的所有性质的,所以对于违反性质2),我们只要使得让其根节点变成黑色的,则整个红黑树的性质是可以满足的。若对于违反性质4)我们只要使得红色节点x变成黑色的,则整棵树又满足了红黑树的所有性质。剩下内容就是使得双重颜色节点x变成真正意义上的双重颜色节点,即使得性质5)得以维持,调整过程如下。

case1 x的兄弟w是红色的

case2 x的兄弟w是黑色的,且两个孩子是黑色的

case3 x的兄弟w是黑色的,且w的左孩子是黑色的,右孩子是黑色的

case4 x的兄弟w是黑色的,且w的右孩子是红色的

从上述调整我们可以得出其转化图


尤其情况转化,我们可以得出其伪代码:

    if (case 1) {
        
    }
    if (case 2) {
        
    } else {
        if (case 3) {
            
        }
        case 4
    }
其中可能有人认为会在case1和case2中无限循环,而完成不了调整的目的。

因为case1,使得节点x下沉,而case2使得x节点上升,因此不得不会这样想。但我们多想一下,满足上述情况case1只能转化到case2,而case2只能转化到case1,当我们再往下走时,发现x继续上升,因为此时的x的兄弟节点w是黑色的而不是红色的,所以x会继续上升。因此可以想象整个x是不断朝根节点上升的。

调整代码:

void deleteFixUp(RedBlackTree t, Node *x) {
    while (x != t->root && x->color == BLACK) {
        // x is x's parent's left child
        if (x == x->p->left) {
            Node * w = x->p->right;
            // case1:turn it into case2 or case3 or case4
            if(w->color == RED) {
                w->color = BLACK;
                x->p->color = RED;
                leftRotate(t,x->p);
                w = x->p->right;
            }
            //case 2
            if(w->left->color == BLACK && w->right->color == BLACK) {
                w->color = RED;
                x = x->p;
            } else {
                // case 3
                if(w->right->color == BLACK) {
                    w->color = RED;
                    w->left->color = BLACK;
                    rightRotate(t, w);
                    w = x->p->right;
                }
                // case 4
                w->color = x->p->color;
                x->p->color = BLACK;
                w->right->color = BLACK;
                leftRotate(t,x->p);
                x = t->root;
            }
        } else {
            Node *w = x->p->left;
            if (w->color == RED) {
                w->color = BLACK;
                x->p->color = RED;
                rightRotate(t, x->p);
                w = x->p->left;
            }
            if (w->left->color == BLACK && w->right->color == BLACK) {
                w->color = RED;
                x = x->p;
            } else {
                if(w->left->color == BLACK) {
                    w->color = RED;
                    w->right->color = BLACK;
                    leftRotate(t, w);
                    w = x->p->left;
                }
                w->color = x->p->color;
                x->p->color = BLACK;
                w->left->color = BLACK;
                rightRotate(t, x->p);
                x = t->root;
            }
        }
    }
    x->color = BLACK;
}




至此,所有的分析到此为止,有错误的和疑惑的请多多留言。


完整代码请转此



参考

本文主要是依据 Thomas H.Cormen、Charles E.Leiserson《算法导论》第二版 第十三章 红黑树


转载请注明

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值