数据结构之红黑树

1、基本概念

  红黑树是一种平衡二叉查找树,不同于AVL树,红黑树必须满足一下几个规则:
1. 树中的节点不是为红色就是为黑色;
2. 根节点的颜色为黑色;
3. 若节点的颜色为红色,则其子节点必然为黑色;
4. 任意节点至NULL的路径上黑色节点的数目是相同的。
  根据规则4,可知新增节点必须为红色;根据规则2和3,若节点为红色,则其父节点必然为黑色;根据规则3,若插入节点的父节点为红色,就需要进行平衡调整。

2、节点结构

  红黑树的操作过程需要经常性地访问节点的父节点,因而相对于二叉树的节点结构,这里需要增加指向父节点的指针。同时,红黑树的节点还需要存储该节点的颜色值。这里我还加了子节点位置的定义,可以方便我们获取节点的兄弟节点以及判断节点属于外侧还是内侧。具体定义如下:

enum Color
{
    RED,
    BLACK
};

enum ChildType
{
    NONE,
    LEFT,
    RIGHT
};

struct RBTreeNode:BTreeNode
{
    RBTreeNode():BTreeNode(),parent(NULL),color(RED){}

    RBTreeNode(int data,RBTreeNode* leftChild = NULL,RBTreeNode* rightChild = NULL)
        :BTreeNode(data,leftChild,rightChild),parent(NULL),color(RED)
    {

    }

    RBTreeNode*& getLeftChild()
    {
        return (RBTreeNode*&)leftChild;
    }

    RBTreeNode*& getRightChild()
    {
        return (RBTreeNode*&)rightChild;
    }

    RBTreeNode*& getChildByType(ChildType type)
    {
        return type == LEFT?getLeftChild():getRightChild();
    }

    RBTreeNode* getSibling()
    {
        ChildType type = getChildType();
        switch (type)
        {
        case LEFT:
            return (RBTreeNode*)parent->rightChild;
        case RIGHT:
            return (RBTreeNode*)parent->leftChild;
        default:
            break;
        }
        return NULL;
    }

    ChildType getChildType()
    {
        if (parent != NULL)
        {
            return data < parent->data?LEFT:RIGHT;
        }
        return NONE;
    }

    void handleChildren()
    {
        if (getLeftChild() != NULL)
        {
            getLeftChild()->parent = this;
        }

        if (getRightChild() != NULL)
        {
            getRightChild()->parent = this;
        }
    }

    RBTreeNode* parent;
    Color color;
};

3、旋转操作

  红黑树与AVL树一样,需要借助旋转操作进行树的平衡,但红黑树的节点还定义了指向父节点的指针,这个在旋转过程中也需要对应调整,具体实现如下:

void RBTree::rotateWithLeftChild(RBTreeNode* node)
{
    RBTreeNode* leftChild = node->getLeftChild();

    node->leftChild = leftChild->rightChild;
    if (leftChild->rightChild != NULL)
    {
        leftChild->getRightChild()->parent = node;
    }

    leftChild->parent = node->parent;
    if (node->getChildType() == NONE)
    {
        m_pRoot = leftChild;
    } 
    else if (node->getChildType() == LEFT)
    {
        node->parent->leftChild = leftChild;
    }
    else
    {
        node->parent->rightChild = leftChild;
    }

    leftChild->rightChild = node;
    node->parent = leftChild;
}

void RBTree::rotateWithRightChild(RBTreeNode* node)
{
    RBTreeNode* rightChild = (RBTreeNode*)node->rightChild;

    node->rightChild = rightChild->leftChild;
    if (rightChild->leftChild != NULL)
    {
        rightChild->getLeftChild()->parent = node;
    }

    rightChild->parent = node->parent;
    if (node->getChildType() == NONE)
    {
        m_pRoot = rightChild;
    } 
    else if (node->getChildType() == LEFT)
    {
        node->parent->leftChild = rightChild;
    }
    else
    {
        node->parent->rightChild = rightChild;
    }

    rightChild->leftChild = node;
    node->parent = rightChild;
}

void RBTree::doubleWithLeftChild(RBTreeNode* node)
{
    rotateWithRightChild(node->getLeftChild());
    rotateWithLeftChild(node);
}

void RBTree::doubleWithRightChild(RBTreeNode* node)
{
    rotateWithLeftChild(node->getRightChild());
    rotateWithRightChild(node);
}

4、查找操作

  红黑树的查找操作与二叉查找树的查找操作一致,直接复用。

5、插入操作

  红黑树的插入算法与二叉查找树的插入算法一致,但插入节点后,需要根据不同的情况进行不同的操作,插入情况分以下几点:
1. 父节点为空。插入节点的父节点为空,则可知插入节点为根节点,根据规则2将其设置为黑色;
这里写图片描述
2. 黒父。插入节点的父节点为黑色,此时没有违反任何规则,无需进行平衡处理;
这里写图片描述
3. 红父红叔。插入节点的父节点和叔父节点的颜色为红色,根据规则可知祖父节点必然为黑色。此时无需旋转,只用将祖父节点设置为红色,将父节点和叔父节点的颜色设置为黑色,祖父节点达到平衡。但是祖父节点可能为根节点或者引起其父节点不平衡,因而需要将祖父节点作为插入节点进行平衡处理;
这里写图片描述
4. 红父黑叔。插入节点的父节点为红色,叔父节点为空或者黑色,根据规则可知祖父节点必然为黑色。此时根据插入节点的插入位置分为外侧插入和内侧插入:
  4.1外侧插入。若插入节点和父节点都是其对应父节点的左子女或者右子女,则为外侧插入。此时将祖父节点设置为红色,父节点的颜色设置为黑色,然后对祖父节点进行单旋转,使祖父节点成为父节点的子节点,达到平衡;
这里写图片描述
  4.2内侧插入。若插入节点和父节点处于其对应父节点的不同子女位置,则为内侧插入。此时将祖父节点设置为红色,插入节点的颜色设置为黑色,然后对祖父节点进行双旋转,使祖父节点成为插入节点的子节点,达到平衡。
这里写图片描述
  以下为插入算法和平衡算法的具体实现:

void RBTree::_insert(int key, BTreeNode* &node)
{
    RBTreeNode* &curNode = (RBTreeNode* &)node;
    if (curNode == NULL)
    {
        curNode = new RBTreeNode(key);
        balanceTreeNode(curNode);
    }
    else
    {
        if (key < curNode->data)
        {
            if (curNode->leftChild != NULL)
            {
                _insert(key, node->leftChild);
            } 
            else
            {
                curNode->leftChild = new RBTreeNode(key);
                curNode->getLeftChild()->parent = curNode;

                balanceTreeNode(curNode->getLeftChild());
            }
        } 
        else if (key > curNode->data)
        {
            if (curNode->rightChild != NULL)
            {
                _insert(key, node->rightChild);
            } 
            else
            {
                curNode->rightChild = new RBTreeNode(key);
                curNode->getRightChild()->parent = curNode;

                balanceTreeNode(curNode->getRightChild());
            }
        }
        else
        {

        }
    }
}

void RBTree::balanceTreeNode(RBTreeNode* node)
{
    if (node == NULL)
    {
        return;
    }

    //插入节点为根节点,根节点必须为黑色
    if (node->parent == NULL)
    {
        node->color = BLACK;
        return;
    }

    //插入节点的父节点为黑色,不破坏平衡
    if (node->parent->color == BLACK)
    {
        return;
    }

    RBTreeNode* parent = node->parent;
    RBTreeNode* grand = parent->parent;
    RBTreeNode* uncle = parent->getSibling();

    //伯父节点为空或者黑色
    if(uncle == NULL || uncle->color == BLACK)
    {

        if(node->getChildType() == parent->getChildType())
        {
            //外侧插入
            grand->color = RED;
            parent->color = BLACK;
            if(node->getChildType() == LEFT)
            {
                rotateWithLeftChild(grand);
            }
            else
            {
                rotateWithRightChild(grand);
            }
        }
        else
        {
            //内侧插入
            grand->color = RED;
            node->color = BLACK;
            if(node->getChildType() == LEFT)
            {
                doubleWithRightChild(grand);
            }
            else
            {
                doubleWithLeftChild(grand);
            }
        }
    }
    else
    {
        //无需旋转,直接改变颜色,然后对祖父节点进行平衡操作
        grand->color = RED;
        parent->color = BLACK;
        uncle->color = BLACK;

        balanceTreeNode(grand);
    }
}

6、删除操作

  红黑树的删除算法与二叉查找树的删除算法一致,因此删除的节点必然为单支节点或者叶子节点。但删除节点后,需要进行平衡检测,删除情况分为以下几种:
1. 删除节点为红色。此时根据红黑树规则,删除节点必然为叶子节点,删除后不破坏平衡规则,直接删除即可;
这里写图片描述
2. 删除节点为黑色。删除该节点后,会选子节点作为替代节点。若替代节点为红色,此时所有经过子节点的路径上的黑色节点的数目都减少了一个,只需将子节点的颜色设置为黑色即可达到平衡;
这里写图片描述
3. 替换节点为空或者黑色。根据红黑树规则,黑色节点只有一个黑色子节点的情况是不存在的。同时,若删除节点不为根节点,则其兄弟节点必然不为空。此时又有以下几种情况:
  3.1替换节点成为根节点。此时不做处理;
  3.2红兄。删除节点的兄弟节点为红色,此时父节点的颜色为黑色,兄弟节点的子节点也必然为黑色,将父节点的颜色设置为红色,兄弟节点的颜色设置为黑色,并对父节点进行单旋转,使父节点成为兄弟节点的子节点,转换为黑兄情况;
这里写图片描述
  3.3黑兄二黑侄。删除节点的兄弟节点为黑色,同时兄弟节点没有子节点或者有两个黑色子节点,此时将兄弟节点的颜色设置为红色,父节点达到平衡。但是父节点的颜色不确定,需要把父节点作为新的替换节点进行平衡处理;
这里写图片描述
  3.4黑兄内侧红侄。此时设置兄弟节点的颜色为红色,红侄的颜色设置为黑色,并对兄弟节点进行单旋操作,使红侄成为新的兄弟节点,转为下一种情况;
这里写图片描述
  3.5黑兄外侧红侄。此时设置兄弟节点的颜色为父节点的颜色,设置父节点的颜色为黑色,设置红侄节点的颜色为黑色,然后对父节点进行单旋操作,使兄弟节点成为新的父节点,达到平衡。
这里写图片描述
  以下是删除算法和删除平衡算法的具体实现:

void RBTree::_remove(int key, BTreeNode* &node)
{
    if (node == NULL)
    {
        return;
    }

    if (key == node->data)
    {
        if (node->leftChild != NULL && node->rightChild != NULL)
        {
            //替换键值
            BTreeNode * temp = _findMin(node->rightChild);
            node->data = temp->data;
            //递归删除
            _remove(temp->data,node->rightChild);
        } 
        else
        {
            RBTreeNode* &curNode = (RBTreeNode* &)node;
            RBTreeNode* temp = curNode;
            RBTreeNode* parent = curNode->parent;
            RBTreeNode* sibling = curNode->getSibling();
            curNode = (curNode->getLeftChild() != NULL)?curNode->getLeftChild():curNode->getRightChild();
            if (curNode != NULL)
            {
                curNode->parent = parent;
            }

            if (temp->color == RED)
            {
                //红色节点直接删除
                delete temp;
            }
            else
            {
                balanceTreeNodeRemove(curNode,parent);
                delete temp;
            }
        }
    }
    else if (key < node->data)
    {
        _remove(key,node->leftChild);
    }
    else
    {
        _remove(key,node->rightChild);
    }
}

void RBTree::balanceTreeNodeRemove(RBTreeNode* node, RBTreeNode* parent)
{
    while (true)
    {
        //根节点,不再处理
        if (node == m_pRoot)
        {
            break;
        }

        //红色节点,直接变为黑色
        if (node != NULL && node->color == RED)
        {
            node->color = BLACK;
            break;
        }

        RBTreeNode* sibling = parent->getLeftChild() == node?
            parent->getRightChild():parent->getLeftChild();

        if (sibling->color == RED)
        {
            //兄弟节点为红色,转为兄弟节点为黑色的情况
            parent->color = RED;
            sibling->color = BLACK;
            if (sibling->getChildType() == LEFT)
            {
                rotateWithLeftChild(parent);
            } 
            else
            {
                rotateWithRightChild(parent);
            }

            //重新获取兄弟节点
            sibling = parent->getLeftChild() == node?
                parent->getRightChild():parent->getLeftChild();
        }

        //黑兄二黑侄
        if ((sibling->leftChild == NULL || sibling->getLeftChild()->color == BLACK) &&
            (sibling->rightChild == NULL || sibling->getRightChild()->color == BLACK))
        {
            //黑兄二黑侄红父或黑兄二黑侄黑父
            sibling->color = RED;
            node = parent;
            parent = node->parent;
        }
        else
        {
            ChildType type = sibling->getChildType() == LEFT?RIGHT:LEFT;
            RBTreeNode* child = sibling->getChildByType(type);
            //黑兄内侧红侄
            if (child != NULL && child->color == RED)
            {
                sibling->color = RED;
                if (type == LEFT)
                {
                    sibling->getLeftChild()->color = BLACK;
                    rotateWithLeftChild(sibling);
                    sibling = sibling->getLeftChild();
                } 
                else
                {
                    sibling->getRightChild()->color = BLACK;
                    rotateWithRightChild(sibling);
                    sibling = sibling->getRightChild();
                }
            }

            //黑兄外侧红侄
            sibling->color = parent->color;
            parent->color = BLACK;
            type = sibling->getChildType();
            if (type == LEFT)
            {
                if (sibling->getLeftChild() != NULL)
                {
                    sibling->getLeftChild()->color = BLACK;
                }
                rotateWithLeftChild(parent);
            } 
            else
            {
                if (sibling->getLeftChild() != NULL)
                {
                    sibling->getLeftChild()->color = BLACK;
                }
                rotateWithRightChild(parent);
            }
            break;
        }
    }
}

5、清理操作

  红黑树的清理操作与二叉树的清理操作一致,直接复用。

6、结果测试

  这里对红黑树进行具体的测试,代码如下:

RBTree* rbTree = new RBTree();
rbTree->insert(3);
rbTree->insert(2);
rbTree->insert(4);
rbTree->insert(1);
rbTree->insert(5);
rbTree->insert(6);
rbTree->insert(7);
rbTree->insert(8);
rbTree->insert(9);

cout<<"contains 5:"<<rbTree->contains(5)<<endl; //contains 5:1
cout<<"contains 10:"<<rbTree->contains(10)<<endl; //contains 10:0

rbTree->breadthFirstTravel();  //bft:5 3 7 2 4 6 8 1 9
rbTree->depthFirstTravel_DLR(); //depthFirstTravel_DLR:5(b) 3(r) 2(b) 1(r) 4(b) 7(r) 6(b) 8(b) 9(r)
rbTree->depthFirstTravel_LDR(); //depthFirstTravel_LDR:1 2 3 4 5 6 7 8 9
rbTree->depthFirstTravel_LRD(); //depthFirstTravel_LRD:1 2 4 3 6 9 8 7 5

rbTree->remove(7);
rbTree->breadthFirstTravel();  //bft:5 3 8 2 4 6 9 1
rbTree->depthFirstTravel_DLR(); //depthFirstTravel_DLR:5(b) 3(r) 2(b) 1(r) 4(b) 8(r) 6(b) 9(b)
rbTree->depthFirstTravel_LDR(); //depthFirstTravel_LDR:1 2 3 4 5 6 8 9
rbTree->depthFirstTravel_LRD(); //depthFirstTravel_LRD:1 2 4 3 6 9 8 5

rbTree->clear();
rbTree->breadthFirstTravel();  //bft:

6、效率分析

  红黑树与AVL树效率一致,但其平衡性没有AVL树那么严格,能够在保持平衡的情况下出现更少需要进行平衡调整的情况,因而拥有更好的统计性能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
红黑树是一种自平衡的二叉查找树,它在插入和删除节点时能够保持树的平衡。红黑树的概念可以参考。在Java中实现红黑树,可以按照以下步骤进行: 1. 首先将红黑树当作一颗二叉查找树,将新节点插入到适当的位置上。 2. 将插入的节点着色为"红色"。 3. 根据红黑树的特性,通过一系列的旋转和着色等操作,使树重新保持红黑树的性质。 具体的插入过程可以参考中提供的代码。在代码中,使用了左旋转、右旋转和颜色翻转等操作来重新平衡红黑树。 首先,如果节点的右子树是红色而左子树是黑色,可以通过左旋转操作将其变为左子树为红色,右子树为黑色的情况。 其次,如果节点的左子树和左子树的左子树都是红色,可以通过右旋转操作将其修正为上述情况。 最后,如果节点的左子树和右子树都是红色,可以通过颜色翻转操作将其修正为左子树和右子树都为黑色的情况。 在插入完节点后,需要将根节点的颜色设置为黑色,以确保红黑树的性质满足。 这样,通过以上的步骤,就能够实现对红黑树的插入操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java数据结构红黑树的真正理解](https://download.csdn.net/download/weixin_38622475/12770272)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java高阶数据结构红黑树](https://blog.csdn.net/qq15035899256/article/details/126678970)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java数据结构——红黑树](https://blog.csdn.net/weixin_30699463/article/details/95256212)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值