红黑树的插入和删除

一、RedBlackTree class defination

template<class T>
class NodeT
{
public:
    T data;
    NodeT* left;
    NodeT* right;
    NodeT* parent;
    bool isBlack; // determine whether the node is red or black. true = black, false = red

    // constructor
    NodeT<T>(T value) : data(value), left(nullptr), right(nullptr), parent(nullptr), isBlack(false) {;}
};

template<class T>
class RedBlackTree
{
private:
    NodeT<T>* root;
    int length;
    // POST: finds the value in the tree and returns it node, if no such value returns nullptr
    NodeT<T>* findNode(T value) const;
    // POST: find the target's predecessor in the tree
    NodeT<T>* getPredecessor(NodeT<T> *target) const;

    // POST: binary search tree insert algorithm
    bool BST_insert(NodeT<T>* newNode);

    // POST: left rotate the tree along x
    void leftRotate(NodeT<T> *x);
    // POST: right rotate the three along x
    void rightRotate(NodeT<T> *x);
    // PARAM: newNode - the new node after delete a black node
    //        newNodeParent - the parent of new node
    //        isLeft - is the new node a left or right child
    // POST: fix the tree after delete a black node
    void rbFix(NodeT<T>* newNode, NodeT<T>* newNodeParent);

public:
    // default constructor -- size = 0; root = nullptr
    RedBlackTree();

    // POST: if the parameter is not already in the tree, inserts it and returns true; otherwise 
    //       does nothing and returns false
    // PARAM: the value to be inserted
    bool insert(T value);

    // POST: if the parameter exists in tree, deletes it and returns true; otherwise returns false
    // PARAM: the value to be removed
    bool remove(T value);
};

二、红黑树的插入

代码如下:

*helper method都会放在文章的最下面​​​​​​​

template<class T>
bool RedBlackTree<T>::insert(T value) 
{
    NodeT<T>* newNode = new NodeT<T>(value);

    // if value is already in the tree; returns false
    if(BST_insert(newNode) == false){
        return false;
    }

    // if new node's parent is red
    while(newNode != root && newNode->parent->isBlack == false){

        NodeT<T> *uncle;
        if(newNode->parent->parent->right == newNode->parent){ // new node's parent is a right child; uncle is left child
            uncle = newNode->parent->parent->left;
        
            // if new node's uncle is red, new node's parent and uncle marks with black,
            // its grandparent marks with red
            if(uncle != nullptr && uncle->isBlack == false){ // uncle is red
                
                newNode->parent->isBlack = true;
                uncle->isBlack = true;
                newNode->parent->parent->isBlack = false;

                // if new node's grandparent's parent still is red, 
                // continue loop until gp's parent is black
                newNode = newNode->parent->parent;
            }
            else{ // uncle is black

                // detemine new node is inline or is not inline,
                // if is not inline, rotate along the parent to the inline direction;
                // in this case, right is inline direction
                if(newNode == newNode->parent->left){
                    newNode = newNode->parent;
                    rightRotate(newNode);
                }
                // left rotate along the grandparent to the uncle direction
                newNode->parent->isBlack = true;
                newNode->parent->parent->isBlack = false;
                leftRotate(newNode->parent->parent);
            }
        }
        else{ // uncle is a right child; mirror case
            uncle = newNode->parent->parent->right;
    
            if (uncle != nullptr && uncle->isBlack == false)
            {
                
                newNode->parent->isBlack = true;
                newNode->parent->parent->isBlack = false;
                uncle->isBlack = true;

                newNode = newNode->parent->parent;
            }
            else
            {
                if (newNode == newNode->parent->right)
                {
                    newNode = newNode->parent;
                    leftRotate(newNode);
                }
                newNode->parent->isBlack = true;
                newNode->parent->parent->isBlack = false;
                rightRotate(newNode->parent->parent);
            }
        }
    }

    root->isBlack = true;
    return true;
}

1. 先用二叉树插入新的节点,新的节点标为红色

1.1如果要插入的值已经存在节点中,什么都不做,返回false

2. 当插入的节点的parent也是红色时

为了修复红色的父节点不能有红色的子节点,我们要对他进行修复。修复插入的时候我们要去看插入节点的uncle节点(父节点的兄弟)。

注意:uncle节点也可能是nullptr的叶子节点,为黑色

2.1 如果没有uncle节点,被插入的节点就是根,直接染黑。

2.2 如果uncle是红,parent染黑,grandparent染红,uncle染黑。

例子:

  

插入30

 ​ 

插入节点的parent染黑,gp染红,uncle染黑

经过修复,没有红色的父节点有红色的子节点了

再把grandparent赋值为新插入的节点,继续套进循环,20这个节点没有uncle,所以为根节点,直接染黑

 

2.3 如果uncle是黑色的

2.3.1 如果新插入的节点与他的父节点不在一条线上,沿着parent往inline(变成一条线上的)方向转

插入17

沿着parent向右转

parent: 19  newNode:17  ==> newNode:19 parent:17

现在三个节点就都是inline了 

2.3.2 现在parent和新插入的节点为inline

newNode:19  parent:17  grandparent:15

沿着grandparent往uncle方向进行一次旋转(uncle为nullptr leaf)

 ​​​​​​​

 grandparent和parent交换颜色

三、红黑树的删除

代码如下:

*helper method都会放在文章的最下面​​​​​​​

// POST: if the parameter exists in tree, deletes it and returns true; otherwise returns false
// PARAM: the value to be removed
template<class T>
bool RedBlackTree<T>::remove(T value)
{
    // find target
    NodeT<T>* z = findNode(value);
    if(z == nullptr){
        return false;
    }

    // if red black tree only has on element, so if the only one element can be found, it is root
    if(size() == 1){
        root = nullptr;
        length--;
        return true;
    }
    NodeT<T>* y; 
    NodeT<T>* x;
    NodeT<T>* xParent;
    if(z->left == nullptr || z->right == nullptr){ // z has zero or one children
        y = z; //node to be removed
    } 
    else{ // z has two children
        y = getPredecessor(z); // y is successor
    }
    
    // y only have at most one child, because if y is successor or predecessor, it has zero or one children
    // x is y's only one child, or the nullptr
    if(y->left != nullptr){
        x =  y->left;
    }
    else{
        x = y->right;
    }

    xParent = y->parent;
    if(x != nullptr){ // detach x from y
        x->parent = y->parent;
    }
    if (y->parent == nullptr){ // if y is root
        root = x;
    }
    else{
        // Attach x to y's parent
        if(y == y->parent->left){ // y is left child
            y->parent->left = x;
        }
        else{
            y->parent->right = x; // y is right child
        }
    }

    if(y != z){ // if y is the successor
        z->data = y->data; // replace z with y
    }
    if(y->isBlack == true){
        rbFix(x, xParent);
    }

    delete y;

    length--;
    return true;
}

1. 如果要删掉的节点是红色

非常完美,什么都不用做,直接删了就行,因为删除红色的节点不会影响任何红黑树的属性

2. 如果要删掉的节点是黑色

删掉的节点是黑色的话,会违反红黑树的“所有的路径从根到叶子黑色节点必须一致”这条属性,一条路径删除一个黑色节点,他的黑色节点就比别的路径少一个。

找到要删掉的实际的节点,如果是要删掉的节点有两个子节点的话,把要删掉的节点的successor/predecessor的数据赋值给要删除的节点,然后删除他的predecessor就可以。(删除的其实不是要删的节点,而是要删的节点的predecessor)

2.1 删除节点后接上来的节点是红色,直接染黑就可以

删除25

30接上来,直接染黑 

2.2 删除节点接上来的节点是黑色

这时候删掉节点后接上来的节点,我们就给他起名叫做“doubly black”(双黑),双黑只是我们想象的一个东西,我们要把双黑进行修复,把多余的黑色传到别的地方去。

相比于插入的时候我们看的是uncle节点,删除的时候我们要看他的sibling(兄弟姐妹)

2.2.1 如果sibling是红色

删除30

sibling:14, parent:20

把null leaf看成一个双黑

沿着双黑的parent往双黑的方向转

 

 把双黑的grandparent染黑,sibling染红

 2.2.2 如果sibling是黑色

a. 如果sibling有红色的子节点

 如果sibling的红色child不是inline的话,沿着sibling的inline方向转,把他转成一个inline的

delete 10 

null leaf看成是双黑,双黑的sibling16黑色,有红色的节点,不inline,先转成inline

 沿着parent往双黑方向转

之后把双黑的parent的sibling染黑

 

b. 如果sibling全是黑色的子节点,把双黑传递给parent,把sibling变成红色,一直重复向上传,如果传到root,什么都不用做

删除14

 

sibling染红,双黑重新赋为双黑的父节点(15) 

 

双黑为根节点,什么都不用做,删除完成

Helper method:

// finds the value in the tree and returns it node, if no such value returns nullptr
template<class T>
NodeT<T>* RedBlackTree<T>::findNode(T value) const
{
    NodeT<T> *temp = root;
    while(temp != nullptr){
        if(value == temp->data){
            return temp;
        }
        if(value > temp->data){
            temp = temp->right;
        }
        else{
            temp = temp->left;
        }
    }

    return nullptr;
}

// POST: find the target's predecessor in the tree
// PARAM: target
template<class T>
NodeT<T>* RedBlackTree<T>::getPredecessor(NodeT<T> *nd) const
{
    NodeT<T> *successor = nd->left;
    while(successor->right != nullptr){
        successor = successor->right;
    }

    return successor;
}

// POST: binary search tree insert algorithm
template<class T>
bool RedBlackTree<T>::BST_insert(NodeT<T>* newNode)
{
    NodeT<T> *temp = root;
    while(temp != nullptr){ // find parent of new node
        if(newNode->data > temp->data){
            newNode->parent = temp;
            temp = temp->right;
        }
        else if(newNode->data < temp->data){
            newNode->parent = temp;
            temp = temp->left;
        }
        else{
            return false;
        }
    }
    if(root == nullptr){
        root = newNode;
    }
    else{ // insert new node
        if(newNode->data > newNode->parent->data){    
            newNode->parent->right = newNode;
        }
        else{
            newNode->parent->left = newNode;
        }
    }

    length++;
    return true;
}
// POST: left rotate the tree along x
template<class T>
void RedBlackTree<T>::leftRotate(NodeT<T>* x)
{
    NodeT<T>* y = x->right;
    x->right = y->left;
    if (y->left != nullptr){
        y->left->parent = x;
    }
    y->parent = x->parent;
    if (x->parent == nullptr){
        this->root = y;
    }
    else if (x == x->parent->left){
        x->parent->left = y;
    }
    else{
        x->parent->right = y;
    }
    y->left = x;
    x->parent = y;
}

 // POST: right rotate the three along x
template<class T>
void RedBlackTree<T>::rightRotate(NodeT<T>* x)
{
    NodeT<T>* y = x->left;
    x->left = y->right;
    if (y->right != nullptr){
        y->right->parent = x;
    }
    y->parent = x->parent;
    if (x->parent == nullptr){
        root = y;
    }
    else if (x == x->parent->right){
        x->parent->right = y;
    }
    else{
        x->parent->left = y;
    }
    y->right = x;
    x->parent = y;
}

// PARAM: newNode - the new node after delete a black node
//        newNodeParent - the parent of new node
//        isLeft - is the new node a left or right child
// POST: fix the tree after delete a black node
template<class T>
void RedBlackTree<T>::rbFix(NodeT<T>* x, NodeT<T>* xParent)
{
    NodeT<T>* sibling;
    while(x != root && (x == nullptr || x->isBlack == true)){ // x is not the root and is black child
        if(x == xParent->left){ // if x is left child
            sibling = xParent->right; // x's sibling is a right child
            if(sibling->isBlack == false){ // make sibling inline with x
                sibling->isBlack = true;
                xParent->isBlack = false;
                leftRotate(xParent);
                sibling = xParent->right;
            }

            // if sibling's children are all black
            if((sibling->left == nullptr || sibling->left->isBlack == true) && (sibling->right == nullptr  || sibling->right->isBlack == true)){
                sibling->isBlack = false;
                x = xParent;
                xParent = xParent->parent;
            }
            else{ // one sibling child is red
                if(sibling->right == nullptr || sibling->right->isBlack == true){ // sibling left child is red; red node is not inline, and make it to inline
                    sibling->left->isBlack = true;
                    sibling->isBlack = false;
                    rightRotate(sibling); // make it to inline
                    sibling = xParent->right;
                }
                sibling->isBlack = xParent->isBlack;
                xParent->isBlack = true;
                sibling->right->isBlack = true;
                leftRotate(xParent);
                x = root;
            }
        }
        else{ // symmetric case
            sibling = xParent->left;
            if(sibling->isBlack == false){
                sibling->isBlack = true;
                xParent->isBlack = false;
                rightRotate(xParent);
                sibling = xParent->left;
            }
            if((sibling->left == nullptr || sibling->left->isBlack == true) && (sibling->right == nullptr  || sibling->right->isBlack == true)){
                sibling->isBlack = false;
                x = xParent;
                xParent = xParent->parent;
            }
            else{
                if(sibling->left == nullptr || sibling->left->isBlack == true){
                    sibling->right->isBlack = true;
                    sibling->isBlack = false;
                    leftRotate(sibling);
                    sibling = xParent->parent;
                }
                sibling->isBlack = xParent->isBlack;
                xParent->isBlack = true;
                sibling->left->isBlack = true;
                rightRotate(xParent);
                x = root;
            }
        }
    }
    x->isBlack = true;
}


 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值