红黑树插入删除情况图片总结,帮助你快速回忆。

总是忘记插入和删除的几种情况, 在这做个笔记,其实需要记忆的东西不多,只需要记住插入的两大类情况,删除的三大类情况,就能很好的理解红黑树。

平衡条件

1.节点非黑即红
2.根节点是黑色
3.叶子(NIL)结点是黑色
4.红色结点下边接两个黑色结点
5.从根节点到叶子结点路径上,黑色结点数量相同

第4和第5个条件,决定了红黑树种最长路径最短路径长度的2

红黑树优点

1.与AVL树相比,红黑树比AVL树树高控制条件更松散,红黑树在发生节点插入和删除以后,发生调整的概率,比AVL树要更小。 因为红黑树做的是近似平衡,没有要求高度平衡(AVL树则是), 所以它的维护成本上比AVL树低
2.红黑树的插入,删除,查找操作性能比较稳定,很均衡,都是近似log(n),适合我们对性能要求很严格的时候用,比如不能忍受hash的rehash操作,不能忍受AVL树的大量调整场景。
总结:优点是性能均衡,但是没有明显的缺点。

什么是rehash?
比如在redis中:
redis中有两个哈希表,其中一个时正常使用的 ht[0],另一个是在扩容或者收缩时才需要的ht[1],一般为空。
redis中的哈希表是有负载因子的。负载因子 = 哈希表已保存节点数量 / 哈希表大小, 当负载因子达到一定条件,就会扩容(扩容就会rehash),
先将保存在ht[0]中的所有键值对 rehash到ht[1]中(这个过程如何进行? 每次需要访问数据的时候,先在ht[0]中找,找到了再rehash到ht[1],当ht[0]包含的所有键值对都迁移到了ht[1]后(ht[0]变为空表),释放ht[0],将ht[1]设置为ht[0],并将ht[1]新建一个空白哈希表,为下一次rehash做准备。)
rehash就是重新计算key的hash值和索引值,然后将键值对放置到ht[1]哈希表的指定位置上。

插入策略

理解红黑树的插入调整,站在"祖父接节点“向下进行调整。目的是为了解决双红的情况,新插入的节点一定是红色,因为如果插入黑色节点就一定会产生冲突(黑高不一致)。
情况1.当叔叔节点为红色的时候,修改三元组颜色,改成红黑黑
在这里插入图片描述
从图中可以看到,my和father产生冲突,若此时uncle也是红色,则直接将祖父变成red,father和uncle变成black即可恢复平衡,如下图,不是说红黑黑吗?怎么变成黑黑黑了,因为红黑树的特性是根节点必须是黑色
在这里插入图片描述

情况2.uncle不是red,LL情况(第一个L指father,第二个L指孙子)
只需将祖父节点右旋,左旋后,father变到当前祖父节点的位置,将最上边的三元组变成红黑黑

在这里插入图片描述

祖父节点右旋后,最上边的三元组变成红黑黑,但因为是头节点,所以再把头节点弄黑
在这里插入图片描述

情况3.
LR情况(第一个L指father,第二个L指孙子)
在这里插入图片描述

只需要对father节点进行左旋,变成情况二,就是一样的处理方式了
我们对father进行左旋后
在这里插入图片描述这就变成情况二了,然后跟情况二一样的处理方式即可恢复正常红黑树。

学到这里,我们自然知道,这里只是演示了左边的情况,对于右边出现异常,完全就是左边情况的镜像。(RR同理LL,RL同理LR)

删除策略

红黑树的删除跟插入不太一样,上边讲插入的时候,我们的视角是站在祖父节点,而删除的时候,我们的视角是站在father节点,标记有x的节点表明是双黑节点。
前提:
1.删除红色节点,不会对红黑树的平衡产生影响。
2.度为1的黑色节点,唯一子孩子一定是红色(如果只有一个孩子还是黑色就一定不平衡了)
3.删除度为1的黑色节点,不会产生删除旋转调整(因为度为1的黑色节点的孩子一定是红色, 删除当前黑色,把它的孩子(红色)变成黑色返回上去,就相当于黑色数量依旧保持平衡,无序旋转调整)。
4.删除度为0的黑色节点,会产生一个双重黑的NIL节点
5.删除调整,就是为了干掉双黑节点。
情况1:
双黑节点(对于插入情况的孙子节点)的兄弟是黑色,并且兄弟节点下边的两个子节点也是黑色,那么父亲(对应插入情况的father)节点增加一层黑色,双重黑与兄弟节点,分别减少一层黑色。

在这里插入图片描述情况2:
双黑节点的兄弟节点是黑色,并且兄弟节点的右边是红色
在这里插入图片描述
情况3:
双黑节点的兄弟节点是黑色,并且兄弟节点的左边是红色,这时候需要对兄弟节点左旋变成情况二
如何才能做到左旋变成情况二呢?

伪代码,father指的是38那个节点。
brother->color = red;
father->right = left_rotate(brother);
father->right->color = black;

在这里插入图片描述情况4:
双黑节点的兄弟是红色,这种情况下,对父节点左旋,此时有一个细节,对于上边的情况,处理完毕后都是直接return root,但当前这种情况需要return erase_maintain(root->left); 表示把root->left继续传入删除调整函数,因为这里如果直接return root 就会丢失对这个双黑节点的处理
此处完成左旋后就会变成上述的情况一。然后再继续用处理情况一的方法来解决。
在这里插入图片描述
对于删除情况我们只考虑了双黑节点在左边的情况,对于在右边的情况完全就是镜像,情况四的图只是为了给大家演示遇到这种情况如何调整,这个图上的情况没有满足红黑树的要求(每条路的黑色节点数相同),但是方法是没错的

如有任何问题欢迎评论区留言~

附上完整代码:

#include <iostream>
#include <string>
#include <stdlib.h>
#include <vector>
using namespace std;
#define NIL &(node::__NIL)
struct node{
    node(int key=0,int color=0,node *lchild=NIL,node *rchild=NIL):key(key),color(color),lchild(lchild),rchild(rchild){}
    int color;
    int key;
    node *lchild;
    node *rchild;
    static node __NIL;
};
node node::__NIL(0, 1);

bool has_red_child(node *root){
    return !root->lchild->color || !root->rchild->color;
}
node *left_rotate(node *root){
    node *temp = root->rchild;
    root->rchild = temp->lchild;
    temp->lchild = root;
    return temp;
}
node *right_rotate(node *root){
    node *temp = root->lchild;
    root->lchild = temp->rchild;
    temp->rchild = root;
    return temp;
}
node *insert_maintain(node *root){
    int flag = 0;
    if(root->lchild->color == 0 && has_red_child(root->lchild)) flag = 1;//L
    if(root->rchild->color == 0 && has_red_child(root->rchild)) flag = 2;//R 
    if(flag == 0) return root;
    if(root->lchild->color==0 && root->rchild->color==0){
        root->color = 0;
        root->lchild->color = 1;
        root->rchild->color = 1;
       
    }
    else if(flag == 1){
        node *temp = root->lchild->lchild->color == 0 ? root->lchild->lchild : root->lchild->rchild;
        if(temp == root->lchild->rchild){
            root->lchild = left_rotate(root->lchild);
        }
        root = right_rotate(root);
       
    }
    else if(flag == 2){
        node *temp = root->rchild->rchild->color == 0 ? root->rchild->rchild : root->rchild->lchild;
        if(temp == root->rchild->lchild){
            root->rchild = right_rotate(root->rchild);
        }
        root = left_rotate(root);
       
    }
    root->color = 0;
    root->lchild->color = 1;
    root->rchild->color = 1;
    return root;
    
}
node *GetNewNode(int key){
    return new node(key);
}
node *__insert(node *root, int key){
    if(root == NIL) return GetNewNode(key);
    if(root->key == key) return root;
    if(root->key < key){
       root->rchild = __insert(root->rchild, key);
    }else{
       root->lchild = __insert(root->lchild, key);
    }
    return insert_maintain(root);
}
    node *insert(node *root, int key)
{
    root = __insert(root, key);
    root->color = 1;
    return root;
}
void print(node *root){
    printf("( %d | %d, %d, %d)\n",
    root->color,root->key,root->lchild->key,root->rchild->key);
    return;
}
void output(node *root){
    if(root == NIL) return;
    print(root);//采用先序方便观看树的形状
    output(root->lchild);
    
    output(root->rchild);
}

node *predecessor(node *root){  //获得前驱。
    node *temp = root->lchild;
    while(temp->rchild != NIL){
        temp = temp->rchild;
    }
    return temp;
}
node *erase_maintain(node *root){
    if(root->lchild->color != 2 && root->rchild->color !=2 ) return root;
    int flag = 0;
    if(has_red_child(root)){
        root->color = 0;
        if(root->lchild->color == 0){
            root = right_rotate(root);
            flag = 1;
        }else{
            root = left_rotate(root);
            flag = 2;
        }
        root->color = 1;
        if(flag == 1){
            root->rchild = erase_maintain(root->rchild);
        }else{
            root->lchild = erase_maintain(root->lchild);
        }
        return root;
     }
    if(root->lchild->color == 1 && !has_red_child(root->lchild) || 
        root->rchild->color == 1 && !has_red_child(root->rchild)){
         root->color += 1;  //细节点,黑色往上递归,并不是一次解决好。
         root->lchild->color -= 1;
         root->rchild->color -= 1;
         return root;
     }
    if(root->lchild->color == 1){
        root->rchild->color = 1;
        if(root->lchild->lchild->color != 0){
            root->lchild->color = 0;
            root->lchild = left_rotate(root->lchild);
            root->lchild->color = 1;
        }
        root->lchild->color = root->color;
        root = right_rotate(root);
        } else {
        root->lchild->color = 1;
       
        if(root->rchild->rchild->color != 0){
            root->rchild->color = 0;
            root->rchild = right_rotate(root->rchild);
            root->rchild->color = 1;
        }
         root->rchild->color = root->color;
         root = left_rotate(root);
    }
    root->lchild->color = root->rchild->color = 1;
    return root;
}
node *__erase(node *root, int key){
    if (root == NIL) return root;
   
    if (root->key < key){
        root->rchild = __erase(root->rchild, key);
    } else if (root->key > key){
        root->lchild = __erase(root->lchild, key);
    } else {
        if(root->lchild == NIL || root->rchild == NIL){
            node *temp = root->lchild == NIL ? root->rchild : root->lchild;
            temp->color += root->color;// 细节点
            delete root;
            return temp;
        } else {
            node *temp = predecessor(root);
            root->key = temp->key;
            root->lchild = __erase(root->lchild, temp->key);
        }
    }
    return erase_maintain(root);
}
node *erase(node *root, int key){
    root = __erase(root, key);
    root->color = 1;
    return root;
}
void clear(node *root){
    if(root == NIL) return;
    clear(root->lchild);
    clear(root->rchild);
    delete (root);
    return;
}
int main(){
    int op, val;
    node *root = NIL;
    while(cin >> op >>val){
        switch (op)
        {
        case 1:
            root = insert(root,val);
            break;
        case 2:
            root = erase(root, val);
        default:
            break;
        }
        cout << endl <<"===== rbtree  print =====" << endl;
        output(root);
        cout << "==== rbtree print done ====" << endl;
    }
    clear(root);
    return 0;
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值