1. 红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
2. 红黑树的性质
2.1. 每个结点不是红色就是黑色
2.2. 根节点是黑色的
2.3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(不会出现连在一起的红色节点)
2.4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点(在计算一条路径中黑色节点个数的时候要带上叶子节点,因为叶子节点也是黑色的,也就是空节点)。
2.5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)(为了保证空树也是红黑树)
2.6.红黑树确保没有一条路径会比其他路径长出俩倍(红黑树前面的性质保证了当前的性质)
3. 红黑树的实现
3.1. 带头节点的红黑树
这里我们将红黑树的实现给为带头的红黑树,因为红黑树是map和set的底层数据结构这里我们实现出来红黑树就可以直接用当前我们实现的带头红黑树来实现map和set至于头节点的给出是为了方便于map和set的遍历,在STL中我们区间给出都是左闭右开区间的,既然红黑树作为map和set的底层数据结构那么我们就一定有位置要来放map和set的迭代器,那么就可以将begin位置的迭代器放在head的左,end位置的迭代器可以放在head位置。这里我们将红黑树头节点的颜色给为红色。
3.2. 红黑树的节点
3.3. 红黑树插入节点的分析(实现红黑树最最最关键的一步)
可以看到我们上面在红黑树节点的构造的时候将节点的默认颜色给为红色,那么我们在插入节点的时候就要特别考虑性质3:不可以有两个红色节点连在一起。这里我们可以一共可以分为两大类,一类将节点插入当前红黑树的左子树中,另一类就是将节点插入红黑树的右子树当中。
第一大类(将节点插入红黑树的左子树中)
第一种情况(叔叔节点存在而且为红色,这里将节点插入红黑树的内测还是内测处理方式是一样的)
1.我们插入节点的 parent节点为黑色 :那么这种情况是不需要调整的。
2.我们 插入节点的parent节点为红色 ,而且插入节点的叔叔节点也存在而且为红色的,那么当前节点插入之后就违反了红黑树的性质3,就需要对当前树进行调整。这里解决的时候 我们就将当前parent节点和叔叔节点u的颜色变为黑色。
为什么要这样做呢?
答案:这里我们将cur节点插入之后要解决当前parent和cur颜色都为红色的问题,那么只能将cur和parnet其中一个节点的颜色变为黑色,但是肯定不能将cur节点变为黑色,因为这样在包含cur的路径中就多一个黑色节点,那么我们就要将除包含cur之外的路径中的黑色节点全部都减少一个,又因为此时cur是新插入的节点如果将cur颜色变为黑色那么此时就只有一条路径黑色节点个数+1,如果要调整的话,其他所有节点中黑色节点个数都要减少一个这样肯定是不行的,那么我们只能将parent节点的颜色变为黑色,那么当parent节点变为黑色之后我们可以看到在包含parent的路径中黑色节点增加,但是包含parent节点的路径一定包含parent的双亲节点也就是g节点那么我们将双亲节点g颜色改为红色,那么不就将包含parent路径的黑色节点个数减少一个了吗。然后我们发现又出现新的问题了,就是原本包含u节点的路径因为g节点变为了红色那么包含u节点的路径中少了一个黑色节点(因为包含u节点的路径一定包含g节点)那么我们此时只要将u节点的颜色变为黑色即可。
上面将parent和u的节点更新为红色之后,我们还要考虑g节点让我们更新为红色之后那它的双亲节点是否存在,是否是红色节点:
相关视频推荐
5种红黑树的场景,从Linux内核谈到Nginx源码,听完醍醐灌顶
5种红黑树的用途,从应用到内核场景的优缺点
如果g的双亲不存在:
那么此时g就是根节点那么我们此时需要将g颜色更新为黑色,因为红黑树的根节点必须是黑色的。
如果g的双亲存在:
分为两种情况:1、g的双亲为黑色那么调整结束直接退出。2、如果g的双亲为红色(而且g的叔叔为红色,这里如果g的叔叔为黑色我们下面会讨论)那么我们更新cur和parent继续当前的调整过程。
我们可以总结一下我们的第一种情况:
第二种情况(叔叔节点存在而且一定为黑色或者叔叔节点不存在)
当前cur节点是新插入节点,那么叔叔节点一定是不存在的
当前cur不是新插入节点,那么就和我们第一种情况,我们更新完祖父节点后祖父节点还有叔叔节点的情况这时叔叔节点一定是黑色的,其实下面这种情况的出现就是为了解决上面第一种情况:更新完祖父节点之后祖父节点还有叔叔节点且叔叔节点为黑色。