面试二十一、红黑树

性质: 

插入:

旋转: 

  

删除: 

 情况2.2的删除:
        1. 兄弟是黑色,兄弟的右孩子是红色。

       2.  兄弟是黑色的,兄弟的右孩子不是黑色的,而左孩子是黑色的。

3. 兄弟的节点都是黑色的,没法借,把兄弟节点涂成红色的,然后向上回溯,遇到红色直接涂成黑色,遇不到黑色,即祖先不是红的,让祖先向他的兄弟借,就继续执行这四种情况。没有就把祖先的兄弟的一个节点涂成红色。 如果祖先找到一个红的就涂黑,找不到就回溯,把兄弟涂红,大家都少一个黑色                     最坏情况将所有的兄弟都涂成红色,每条路径少一个黑色。

4. 兄弟是红色,需要变色左旋,然后变成了上面三个情况



#include<iostream>
using namespace std;

template<typename T>
class RBTree{
public:
    RBTree():root_(nullptr){};
    void insert(const T&val){
        if(root_== nullptr){
            root_=new Node(val);
            return ;
        }
        Node *parent = nullptr;
        Node *cur =root_;
        while(cur!= nullptr){
            if(cur->data_>val){
                parent=cur;
                cur=cur->left_;
            }else if(cur->data_<val){
                parent=cur;
                cur=cur->right_;
            }else {
                return;
            }
        }

        // 设置当前节点的parent和颜色
        Node *node =new Node(val,parent, nullptr, nullptr,RED);
        if(parent->data_>val){
            parent->left_=node;
        }else {
            parent->right_=node;
        }

        // 如果新插入的红色节点的父节点也是红色,不满足红黑树的性质,则进行插入调整操作
        if(RED == color(parent)){
            fixAfterInsert(node);
        }
    }
    // 删除操作
    void remove(const T&val){
        if(root_== nullptr){
            return;
        }
        Node* cur =root_;
        while(cur!= nullptr){
            if(cur->data_>val){
                cur=cur->left_;
            }else if(cur->data_<val){
                cur=cur->right_;
            }else{
                break;
            }
        }
        if(cur== nullptr){
            return ;
        }

        // 删除cur节点
        if(cur->left_!= nullptr && cur->right_!= nullptr){
            Node *pre =cur->left_;
            while(pre->right_!= nullptr){
                pre=pre->right_;
            }
            cur->data_=pre->data_;
            cur=pre; // cur指向前驱节点
        }
        // 前驱节点的左孩子或者有孩子
        Node* child =cur->left_;
        if(child== nullptr){
            child=cur->right_;
        }

        if(child!= nullptr){
            child->parent_=cur->parent_;
            if(cur->parent_== nullptr){
                root_=child;
            }else {
                if(cur->parent_->left_==cur){
                    cur->parent_->left_=child;
                }else {
                    cur->parent_->right_=child;
                }
            }
            Color c =color(cur);
            delete cur;
            if(c==BLACK){
                fixAfterRemove(child);
            }
        }else{
             if(cur->parent_== nullptr){
                 delete cur;
                 root_= nullptr;
                 return ;
             }else{
                  //删除cur是叶子
                  if(color(cur)==BLACK){
                      fixAfterRemove(cur);
                  }
                  // 借到之后再删除
                  if(cur->parent_->left_==cur){
                      cur->parent_->left_= nullptr;
                  }else {
                      cur->parent_->right_= nullptr;
                  }
                  delete cur;
             }
        }

    }

private:

    // 节点颜色
    enum  Color{
        BLACK,
        RED
    };
    // 节点类型
    struct Node{
        Node(T data=T() , Node* parent= nullptr , Node *left = nullptr ,Node *right= nullptr,Color color=BLACK)
        :data_(data),left_(left),right_(right),parent_(parent),color_(color){};
        T data_;
        Node *left_;
        Node *right_;
        Node *parent_; // 指向当前节点的父节点
        Color color_;  // 节点的颜色
    };
    // 获取节点颜色
    Color color(Node *node){
        return node== nullptr ? BLACK :node->color_;
    }
    // 设置节点颜色
    void setColor(Node* node,Color color){
        node->color_=color;
    }
    // 返回节点的左孩子
    Node *left(Node *node){
        return node->left_;
    }
    // 返回节点的右孩子
    Node *right(Node *node){
        return node->right_;
    }
    // 返回节点的父亲
    Node* parent(Node *node){
        return node->parent_;
    }
    // 左旋转
    /*
      1. 将孩子的父节点赋值为node的父节点,判断node的父节点是否空,也就是判断node是否为root,如果是就把root赋值为child,否则
    改变node的父节点孩子的值,判断是在父节点的左子树还是右子树
      2. 将child的left赋值给node的右子树,如果child的left不空的话,改变child的parent为node
      3. 改变child的left为node,node的parent为child

 */
    void leftRotate(Node *node){
        Node * child = node->right_;
        child->parent_=node->parent_;
        if(node->parent_== nullptr){
            root_=child;
        }else{
            if(node->parent_->left_==node){
                node->parent_->left_=child;
            }else{
                node->parent_->right_=child;
            }
        }
        node->right_=child->left_;
        if(child->left_!= nullptr){
            child->left_->parent_=node;
        }
        child->left_=node;
        node->parent_=child;
    }
    // 右旋转
    void rightRotate(Node *node){
        Node *child=node->left_;
        child->parent_==node->parent_;
        if(node->parent_== nullptr){
            root_=child;
        }else{
            if(node->parent_->left_==node){
                node->parent_->left_=child;
            }else{
                node->parent_->right_=child;
            }
        }
        node->left_=child->right_;
        if(child->right_!= nullptr){
            child->right_->parent_=node;
        }

        child->right_=node;
        node->parent_=child;
    }
    // 红黑树插入调整操作
    void fixAfterInsert(Node *node){
        // 如果当前红色节点的父节点也是红色,继续调整

        while(color(parent(node))==RED){
            // 判断在爷爷的左子树还是右子树
            if(left(parent(parent(node)))== parent(node)){
                // 插入的节点在左子树中,uncle为空属于黑色
                Node * uncle = right(parent(parent(node)));
                // 情况一
                if(RED == color(uncle)){
                    setColor(parent(node),BLACK);
                    setColor(parent(parent(node)),RED);
                    setColor(uncle,BLACK);

                    // 不知道爷爷和他的父亲是否有冲突,继续向上调整
                    node= parent(parent(node));
                }else {
                        // 先处理情况三
                        if(right(parent(node))==node){
                            node = parent(node);
                            leftRotate(parent((node)));
                        }
                        // 处理情况二
                    setColor(parent(node),BLACK);
                    setColor(parent(parent(node)),RED);
                    rightRotate(parent(parent(node)));
                    // 不用继续处理了,局部性质未变,爷爷也是黑的,所以插入只要旋转两次
                    break;
                }
            }else{
                // 插入的节点在右子树
                Node * uncle = left(parent(parent(node)));
                // 情况一
                if(RED== color(uncle)){
                    setColor(parent(node),BLACK);
                    setColor(parent(parent(node)),RED);
                    setColor(uncle,BLACK);

                    // 不知道爷爷和他的父亲是否有冲突,继续向上调整
                    node= parent(parent(node));
                }else {
                    // 先处理情况三
                    if(left(parent(node))==node){
                        node = parent(node);
                        rightRotate(parent((node)));
                    }
                    // 处理情况二
                    setColor(parent(node),BLACK);
                    setColor(parent(parent(node)),RED);
                    leftRotate(parent(parent(node)));
                    // 不用继续处理了,局部性质未变,爷爷也是黑的,所以插入只要旋转两次
                    break;
                }
            }
        }

        // 强制root为黑色
        setColor(root_,BLACK);
    }
    // 红黑树删除调整操作
    void fixAfterRemove(Node *node){
        while(color(node)==BLACK){
            if(left(parent(node))==node){
                // 删除的黑色节点在左子树
                Node* brother =right(parent(node));
                if(color(brother)==RED){ // 情况四
                    setColor(parent(node),RED);
                    setColor(brother,BLACK);
                    leftRotate(parent(node));
                    brother= right(parent(node));
                }
                if(color(left(brother))==BLACK && color(right(brother))==BLACK){ // 情况三
                    setColor(brother,RED);
                    node = parent(node);
                }else{
                    if(color(right(brother))!=RED){
                        setColor(brother,RED);
                        setColor(left((brother),BLACK));
                        rightRotate(brother);
                        brother= right(parent(node));
                    }
                    // 情况一
                    setColor(brother, color(parent(node)));
                    setColor(parent(node),BLACK);
                    setColor(right(brother),BLACK);
                    leftRotate(parent(node));
                    break;
                }
            }else{
                // 删除的黑色节点在右子树
                Node* brother =left(parent(node));
                if(color(brother)==RED){ // 情况四
                    setColor(parent(node),RED);
                    setColor(brother,BLACK);
                    rightRotate(parent(node));
                    brother= left(parent(node));
                }
                if(color(left(brother))==BLACK && color(right(brother))==BLACK){ // 情况三
                    setColor(brother,RED);
                    node = parent(node);
                }else{
                    if(color(left(brother))!=RED){
                        setColor(brother,RED);
                        setColor(right((brother),BLACK));
                        rightRotate(brother);
                        brother= left(parent(node));
                    }
                    // 情况一
                    setColor(brother, color(parent(node)));
                    setColor(parent(node),BLACK);
                    setColor(left(brother),BLACK);
                    rightRotate(parent(node));
                    break;
                }
            }
        }
        // 如果发现node指向的节点是红色,直接涂成黑色
        setColor(node,BLACK);
    }

    Node *root_; // 指定根节点
};


int main(){


    RBTree<int>rb;
    for (int i = 0; i <=4 ; ++i) {
        rb.insert(i);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值