红黑树

作者: 漫游之光 | 来源:发表于2019-08-28 15:27 被阅读0次

    首先说明一点,这里实现的红黑树,和《算法》(第四版)里面的算法是一样的,不是按照《算法导论》里面的红黑树算法写的。《算法》里面的红黑树是根据2-3查找树来写的。我个人认为2-3查找树里面树向上生长的算法模型非常重要,因为向上生长可以保证空结点到根节点的距离是相同的。可以认为B树是对2-3查找树的推广,而B+树又是对B树的一种优化,B树和B+树在面试里问的概率还是比较大的。

    2-3查找树

    一颗2-3查找树或为一颗空树,或由以下结点组成:

    • 2-结点,含有一个键和两条链接,左链接指向2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。
    • 3-结点,含有两个键和三条链接,左链接指向的2-3树的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。

    一颗完美平衡的2-3查找树中的所有空链接到根结点的距离都应该是相同的。

    2-3查找树的基本操作有3个:查找、插入和删除。

    • 查找:查找算法比较简单,和二叉查找树的算法基本上是一样的,只不过多了对3结点的处理,3结点的处理也比较简单,根据需要查找的值和键的大小,决定去左边、中间、右边还是查找成功。
    • 插入:插入的策略就是先找到要插入的结点的位置,然后才去将插入的结点合并到该结点的策略。可以看出,如果插入的位置是一个2结点,那么插入就结束了。如果是一个3结点,那么将其合并为一个4结点,也就是结点中有3个键。这里我们可以让一个合适的键向上传递,让父节点从2结点变为3结点或者从3结点变为4结点。如果父节点也为4结点,那么也向上传递。可以看出,最终会传递到树根,在树根,我们可以将4结点分裂为3个2结点,使得树高加1。
    • 删除:根据2-3查找树的定义,2-3查找树的最小值和最大值肯定在叶子结点,但这个叶子结点可能是2结点,也有可能是3结点。我们先考虑删除叶子结点的情况,该叶子结点是3结点,那么我们可以直接删除,算法终止。但如果该叶子结点不是3结点,我们没有办法直接删除。这时候,我们可以类比插入,既然一个键可以向上传递,那么肯定也可以向下传递,所以如果从上到下的某个结点为一个3结点,那么我们可以让这个3结点向下传递,这样,最后的叶子结点就为3结点或4结点,可以直接删除。问题是,如果没有3结点怎么办,这时,如果在根结点,根的左右孩子都是2结点,那么可以合成一个4结点,让后让一个键值向下传递。这样,我们就可以保证最后删除的叶子结点肯定为3结点或4结点。如果删除的不是叶子结点,也有办法。我们可以让该结点的前驱或后继来替换这个点,然后删除对应的最小值或最大值。

    红黑树

    红黑二叉查找树背后的基本思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换3-结点)来表示2-3树。我们将树中的链接分为两种类型:红链接将两个2-结点连接起来构成一个3-结点,黑连接则是2-3树中的普通链接。这样,2-3树的一种等价的定义如下:

    • 红链接均为左链接;
    • 没有任何一个结点同时和两条红链接相连;
    • 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。

    代码实现

    #include <iostream>
    #include <cstdlib>
    #include <ctime>
    using namespace std;
    
    class RBTree{
    private:
        static const bool RED = true;
        static const bool BLACK = false;
        struct Node{
            int val;
            Node *left;
            Node *right;
            bool color;
            int height;
            Node(int val_,bool color_):val(val_),color(color_),left(NULL),right(NULL),height(1){}
        };
        Node* root;
    
        bool isRed(Node *x){
            if(x == NULL){
                return false;
            }
            return x->color == RED;
        }
    
        int getHeight(Node *x){
            if(x == NULL){
                return 0;
            }
            return x->height;
        }
    
        /* 如果右子节点是红色的而左子节点是黑色的,进行左旋转(调整为正常的3-结点) */
        Node* rotateLeft(Node *h){
            Node* x = h->right;
            h->right = x->left;
            x->left = h;
            x->color = h->color;
            h->color = RED;
            h->height =  max(getHeight(h->left) , getHeight(h->right)) + 1;
            x->height =  max(getHeight(x->left) , getHeight(x->right)) + 1;
            return x;
        }
    
        /* 如果左子节点是红色的且它的左子节点也是红色的,进行右旋转(调整为正常的4-结点) */
        Node* rotateRight(Node *h){
            Node *x = h->left;
            h->left = x->right;
            x->right = h;
            x->color = h->color;
            h->color = RED;
            h->height =  max(getHeight(h->left) , getHeight(h->right)) + 1;
            x->height =  max(getHeight(x->left) , getHeight(x->right)) + 1;
            return x;
        }
    
        /* 颜色转换,如果将两个子树染黑,当前结点染红,相当于2-3树的分解操作 
        如果将两个子树染黑,当前结点染红,相当于2-3树的合并 */
        void flipColors(Node *h){
            h->color = !h->color;
            h->left->color = !h->left->color;
            h->right->color = !h->right->color;
        }
    
    
        Node* put(Node *h, int val){
            if(h == NULL){
                return new Node(val,RED);
            }
            if(val < h->val){ /* 向左边插入 */
                h->left = put(h->left,val);
            }else if(val > h->val){ /* 向右边插入 */
                h->right = put(h->right,val);
            }else{
                /* 不处理有相同值的情况 */
            }
            if(isRed(h->right) && !isRed(h->left)){
                h = rotateLeft(h);
            }
            if(isRed(h->left) && isRed(h->left->left)){
                h = rotateRight(h);
            }
            if(isRed(h->left) && isRed(h->right)){
                flipColors(h);
            }
            h->height = max(getHeight(h->left) , getHeight(h->right) ) + 1;
            return h;
        }
    
        int minVal(Node* x) { 
            if (x->left == NULL){
                return x->val; 
            } else{
                return minVal(x->left);
            }               
        } 
    
        Node* balance(Node* h) {
            if(isRed(h->right) && !isRed(h->left)){
                h = rotateLeft(h);
            }
            if(isRed(h->left) && isRed(h->left->left)){
                h = rotateRight(h);
            }
            if(isRed(h->left) && isRed(h->right)){
                flipColors(h);
            }
            h->height =max( getHeight(h->left) , getHeight(h->right) ) + 1;
            return h;
        }
    
        /* 从兄弟结点里借一个结点 */
        Node *moveRedLeft(Node *h){
            flipColors(h); /* 合并结点,父节点由3-结点变为2结点或从4-结点变为3-结点 */
            if(isRed(h->right->left)){ /* 兄弟结点为3-结点,借一个,不是,就合并 */
                h->right = rotateRight(h->right);
                h = rotateLeft(h);
                flipColors(h);
            }
            return h;
        }
    
        Node* deleteMin(Node *h){
            if (h->left == NULL){ /* h为红色,如果没有左孩子,可以直接删除 */
                return NULL;
            }
            if (!isRed(h->left) && !isRed(h->left->left)){ /* 左子树为2-结点 */
                h = moveRedLeft(h);
            }
            h->left = deleteMin(h->left);
            return balance(h);
        }
    
        void deleteMin(){
            if(root == NULL){
                return;
            }
            if(!isRed(root->left) && !isRed(root->right)){
                root->color = RED;
            }
            root = deleteMin(root);
            if(root != NULL){
                root->color = BLACK;
            }
        }
    
        Node* moveRedRight(Node *h){
            flipColors(h); /* 合并结点 */
            if(isRed(h->left->left)){ /* 如果左子树为3-结点,从左子树借一个,不合并 */
                h = rotateRight(h);
                flipColors(h);
            }
            return h;
        }
    
        Node* deleteMax(Node *h){
            if(isRed(h->left)){
                h = rotateRight(h);
            }
            if(h->right == NULL){
                return NULL;
            }
            if(!isRed(h->right) && !isRed(h->right->left)){
                h = moveRedRight(h);
            }
            h->right = deleteMax(h->right);
            return balance(h);
        }
    
        void deleteMax(){
            if(root == NULL){
                return;
            }
            if(!isRed(root->left) && !isRed(root->right)){
                root->color = RED;
            }
            root = deleteMax(root);
            if(root != NULL){
                root->color = BLACK;
            }
        }
    
        void destroy(Node *root){
            if(root == NULL){
                return;
            }
            if(root->left != NULL){
                destroy(root->left);
            }
            if(root->right != NULL){
                destroy(root->right);
            }
            delete root;
        }
    
        Node* del(Node *h, int val){
            if(h == NULL){ /* h结点为红色 */
                return NULL;
            }
            if(val < h->val){
                if(h->left != NULL && !isRed(h->left) && !isRed(h->left->left)){
                    h= moveRedLeft(h); /* 向左走,并且左结点为2-结点,先借一个 */
                }
                h->left = del(h->left,val);
            }else{
                if(isRed(h->left)){
                    h = rotateRight(h); /* 两红不合法,右旋 */
                }
                if(val == h->val && h->right == NULL){ /* 删除叶子结点 */
                    return NULL;
                }
                if(h->right != NULL && !isRed(h->right) && !isRed(h->right->left)){
                    h = moveRedRight(h); /* 向右走,并且右结点为2-结点,先借一个 */
                }
                if(val == h->val){
                    h->val = minVal(h->right); /* 使用右子树的最小值来替代当前值 */
                    h->right = deleteMin(h->right); /* 删除右子树的最小值 */
                }else{
                    h->right = del(h->right,val);
                }
            }
            return balance(h);
        }
    
        Node* get(Node *x,int val){
            while (x != NULL) {
                if(val < x->val){
                    x = x->left;
                }else if(val > x->val){
                    x = x->right;
                }else{
                    return x;
                }
            }
            return NULL;
        }
    
    
    public:
    
        RBTree():root(NULL){}
    
        ~RBTree(){
            destroy(root);
        }
    
        bool contains(int val){
            return get(root,val) != NULL;
        }
    
        void put(int val){
            root = put(root,val);
            root->color = BLACK;
        }
    
        void del(int val){
            if(root == NULL){
                return;
            }
            if(!isRed(root->left) && !isRed(root->right)){ /* 合并为一个4结点,因为下一次要调用flipColors */
                root->color = RED;
            }
            root = del(root,val);
            if(root != NULL){
                root->color = BLACK;
            }
        }
    
        bool checkBaLance(){
            if(root != NULL){
                int minHeight = isRed(root->left)?max(getHeight(root->left->left),getHeight(root->left->right)): getHeight(root->left);
                int maxHeight = getHeight(root->right);
                if(minHeight > maxHeight){
                    swap(minHeight,maxHeight);
                }
                
                if(minHeight * 2 < maxHeight){
                    cout<<"error "<<minHeight<<" "<<maxHeight<<endl;
                    return false;
                }
            }
            return true;
        }
    };
    
    
    int main(int argc, char const *argv[])
    {
    
        int n = 1000000;
        srand(time(0));
        RBTree tree;
        for(int i=0;i<n;i++){
            int tmp = rand() % n;
            tree.put(tmp);
            tree.checkBaLance();
        }
        for(int i=0;i<n;i++){
            int tmp = rand() % n;
            tree.del(tmp);
            tree.checkBaLance();
        }
        system("pause");
        return 0;
    }
    

    这个就先贴代码上去吧,这里的主要难度是算法描述和算法实现有相当大的差异,2-3树的算法描述非常简单,但红黑树的实现还是挺令人疑惑的。这里采用的都是一种递归的处理方式,我想可能主要是为了减少理解的难度。

    AVL和红黑树都是通过定义一些平衡的条件,然后通过在插入删除的过程中保持这些条件来达到一种平衡。AVL树是通过旋转,而红黑树则是通过旋转和着色。

    这里的实现对我来说,确实有些困难,所以基本上我都是照着书上的代码写,我又发现了书中的一些错误,上github,发现里面的错误已经修复了,并且,上一次更新就在24天前。从这里,我也意识到了,有Bug是很正常的,但如果发现Bug,我们需要及时修复。

    相关文章

      网友评论

          本文标题:红黑树

          本文链接:https://www.haomeiwen.com/subject/gwarectx.html