数据结构-----红黑树的插入操作

红黑树是一棵二叉搜索树;树种每一个节点的颜色不是黑色就是红色。本篇中只实现用节点的颜色来描述红黑树,性质如下:

RB1:根节点和所有外部节点都是黑色;

RB2:在根至外部节点路径上,没有连续两个节点是红色;

RB3:在所有根至外部节点的路径上,黑色节点的数目都相同;

实现红黑树,首先需要定义树节点,节点包括:

1:节点颜色,黑色或者白色,用整型或者布尔类型表示,也可以预设两个宏;

2:节点权值,用来保存节点的数据;

3:左右子树指针;

首先是红黑树的定义,本例中红黑树继承自BST(二叉搜索树),即:

#define BLACK 1
#define RED 0

template<class T>
class RBTree : public binarySearchTree<T, int>
{
public:
    RBTree() : binarySearchTree() {}
    ~RBTree() {}

    void insert(const T& key) {insert(root, key);}
    void remove(const T& key) {remove(root, key);}

private:
    binaryTreeNode<pair<const T, int>>*
        insert(binaryTreeNode<pair<const T, int>>*&, const T&);

    binaryTreeNode<pair<const T, int>>*
        remove(binaryTreeNode<pair<const T, int>>*&, const T&);

    binaryTreeNode<pair<const T, int>>*
        maximun(binaryTreeNode<pair<const T, int>>*);
    binaryTreeNode<pair<const T, int>>*
        minimun(binaryTreeNode<pair<const T, int>>*);

    int color(binaryTreeNode<pair<const T, int>>*);

}

和AVL树类似,插入删除操作也是利用递归实现,所以在私有成员中存在实现递归调用的重载函数。

模板参数T为节点权值的类型,int为颜色类型。

insert插入函数:在以该节点为根节点的子树中插入新权值,返回插入后该子树的根节点。

remove删除操作:在以该节点为根节点的子树中删除特定权值的节点,返回删除后子树的根节点。

maximun函数:返回以该节点为根节点的子树中权值最大的节点。

minimun函数:返回以该节点为根节点的子树中权值最小的节点。

color函数:返回函数的颜色。

template<class T>
int RBTree<T>::color(binaryTreeNode<pair<const T,int>>* pnode)
{
    if(pnode == NULL)
        return BLACK;
    return pnode->element.second;
}

插入操作步骤如下:
步骤1:找到待插入的位置,即逐个比较权值,若该节点权值小于待插入权值,则在右子树中插入;若该节点权值大于待插入权值,则在左子树中插入。

步骤2:申请新节点,若树为空,则新节点为黑色;若树非空,则新节点为红色。

步骤3:改变相关节点的颜色,调整树平衡。

申请新节点的代码如下:

template<class T>
binaryTreeNode<pair<const T, int>>*
RBTree<T>::insert(binaryTreeNode<pair<const T, int>>* &pnode, const T& key)
{
    if(pnode == NULL)
    {
        if(root == NULL)
            pnode = new binaryTreeNode<pair<const T, int>>*
                        (pair<const T, int>(key, BLACK));
        else
            pnode = new binaryTreeNode<pair<const T, int>>*
                        (pair<const T, int>(key, RED));	
    }
    /*...*/
}

接下来就是执行比较插入的过程,涉及到对节点颜色进行修改的内容,我们先了解一下需要哪些调整。

插入操作的树平衡被破坏无非是存在两个连续的红色节点,违反了RB2。

同时,若违反RB2,则新插入节点和其父节点都应为红色,那么祖父节点必须为黑色(因为插入之前是平衡的)。

被破坏的情况有以下几种,假设子树的根节点为pnode,插入的节点为ptNode:

第一种情况:pnode的左孩子和右孩子都为红色,新节点是pnode孩子的孩子。

插入节点为pnode->leftChild->leftChild或pnode->leftChild->rightChild	
color(pnode->leftChild) == RED && color(pnode->rightChild) == RED  
                              或者					
            
插入节点为pnode->rightChild->leftChild或pnode->rightChild->rightChild	
color(pnode->rightChild) == RED && color(pnode->leftChild) == RED    

解决方案:

1:将根节点的左右子树的颜色涂上黑色;

2:若pnode不是整个红黑树的根节点,则将pnode涂上红色

3:返回pnode;

代码实现:

template<class T>
binaryTreeNode<pair<const T,int>>*
RBTree<T>::rModify(binaryTreeNode<pair<const T, int>>* pnode)
{
    pnode->leftChild->element.second = BLACK;
    pnode->rightChild->element.second = BLACK;
    if(pnode != root)
        pnode->element.second = RED;
    return pnode;
}

第二种情况:pnode的左孩子为红色,右孩子为黑色(也可以是外部节点),新节点是pnode左孩子的孩子。

插入节点为pnode->leftChild->leftChild或pnode->leftChild->rightChild
color(pnode->leftChild) == RED && color(pnode->rightChild) == BLACK 						

解决方案:

1:若插入的是pnode左孩子的左孩子,则进行右旋操作;若插入的是pnode左孩子的右孩子,则进行先左旋后右旋操作;此时pnode为新根节点。

2:将新的根节点涂黑,将新根节点右孩子涂红。

3:返回根节点。

代码实现:

template<class T>
binaryTreeNode<pair<const T,int>>*
RBTree<T>::LbModify(binaryTreeNode<pair<const T, int>>* pnode)
{
    pnode->element.second = BLACK;
    pnode->rightChild->element.second = RED;
    return pnode;
}

函数调用:

if(color(pnode->rightChild) == BLACK && color(pnode->leftChild) == RED)
{
    if(key > pnode->leftChild->element.first)
        pnode = leftRightRotation(pnode);
    else
        pnode = rightRotation(pnode);
    pnode = LbModify(pnode);
}

第三种情况:pnode的右孩子为红色,左孩子为黑色,新节点为pnode右孩子的孩子。

插入节点为pnode->rightChild->leftChild或pnode->rightChild->rightChild	
color(pnode->rightChild) == RED && color(pnode->leftChild) == BLACK  

解决方案:

1:若新节点是pnode右孩子的右孩子,进行左旋操作;若新节点是pnode右孩子的左孩子,进行先右旋后左旋操作,此时pnode为新的根节点。

2:将新的根节点涂黑,新根节点的左孩子涂红。

3:返回新根节点。

代码实现:

template<class T>
binaryTreeNode<pair<const T,int>>*
RBTree<T>::RbModify(binaryTreeNode<pair<const T, int>>* pnode)
{
    pnode->element.second = BLACK;
    pnode->leftChild->element.second = RED;
    return pnode;
}

调用代码:

if(color(pnode->leftChild) == BLACK && color(pnode->rightChild) == RED)
{
    if(key > pnode->rightChild->element.first)
        pnode = leftRotation(pnode);
    else
        pnode = rightLeftRotation(pnode);
    pnode = RbModify(pnode);
}

插入函数代码:

template<class T>
binaryTreeNode<pair<const T, int>>*
RBTree<T>::insert(binaryTreeNode<pair<const T, int>>* &pnode, const T& key)
{
    if(pnode == NULL)	//申请新节点,空则为黑色,非空则为红色。
    {
        if(root == NULL)
            pnode = new binaryTreeNode<pair<const T, int>>*
                        (pair<const T, int>(key, BLACK));
        else
            pnode = new binaryTreeNode<pair<const T, int>>*
                        (pair<const T, int>(key, RED));	
    }
    else
    {
        if(key < pnode->element.first)
        {
            //如果key小,则在左子树中插入
            pnode->leftChild = insert(pnode->leftChild, key);
            //左子树中插入后,若左孩子的孩子是红色,则需要进一步判断。
            if(pnode->leftChild->leftChild != NULL && color(pnode->leftChild->leftChild) == RED)||
               (pnode->leftChild->rightChild != NULL && color(pnode->leftChild->rightChild) == RED)
            {
                //若左孩子是红色,则需要调整
                if(color(pnode->leftChild) == RED)
                {
                    if(color(pnode->rightChild) == RED)	//若右孩子是黑色,为第一种情况
                        pnode = rModify(pnode);
                    else				//否则,为第二种或者第三种
                    {
                        if(key > pnode->leftChild->element.first)
                            pnode = leftRightRotation(pnode);
                        else
                            pnode = rightRotation(pnode);
                        pnode = LbModify(pnode);
                    }
                }
            }
        }
        //与上述对称
        else if(key > pnode->element.first)
        {
            pnode->rightChild = insert(pnode->rightChild, key);
            if(pnode->rightChild->leftChild != NULL && color(pnode->rightChild->leftChild) == RED) ||
                (pnode->rightChild->rightChild != NULL && color(pnode->rightChild->rightChild) == RED)
            {
                if(color(pnode->rightChild) == RED)
                {
                    if(color(pnode->leftChild) == RED)
                        pnode = rModify(pnode);
                    else
                    {
                        if(key > pnode->rightChild->element.first)
                            pnode = leftRotation(pnode);
                        else
                            pnode = rightLeftRotation(pnode);
                        pnode = RbModify(pnode);
                    }
                }
            }
        }
    }

    return pnode;
}

    原文作者:算法小白
    原文地址: https://blog.csdn.net/sinat_35261315/article/details/54100248
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞