红黑树学习笔记之插入操作
红黑树是234树的一种,它是自平衡的二叉查找树,其优点为可以在㏒(n)时间内完成查找,删除和插入的动作。
性质1:节点为有色的(红色和黑色)
性质2:根节点以及所有的叶子节点为黑色
性质3:所有的红色节点的父节点为黑色节点(黑色节点的父节点可以为红色也可以为黑色)
性质4:从任意节点到其简单路径的黑色节点数相同
红黑树简单实例:
注意到叶子节点都是没有数据的,在红黑树中叶子节点为没有数据的黑色节点(满足性质2),以根节点为例到任意的叶子节点的黑色节点为3(满足性质4),根节点为黑色且所有节点都为有色节点满足性质1,2。前面提到红黑树是自平衡的二叉查找树,
所以上图有二叉树的影子。现在以这棵树为例插入一个15的值,根据二叉查找树的性质15>7<18>10>11所以插入位置为11的右孩子的位置,确定插入位置以后还要给他染色(只有红黑可选不可以染成绿色)那么既然可以选红黑怎么选?其实红黑都可以选,但是为了简单起见我们插入一个数据的时候都染成红色。于是我们就有了这样一幅图:
现在请看性质3:所有红色节点的父节点为黑色节点,于是我们违背了性质3;所以我们就要修改一下我们的树使得他继续遵守所有的性质。通常处理这个问题我们有两种办法:其一是变色,其二是旋转。变色也就是改变节点的颜色使得所有节点满足规律,比如我们可以把15变成黑色,当然这没什么用,我们需要将一些其他的节点变色。旋转操作:旋转分为左旋转和右旋转,当然原理是一样的,先来看一个左旋转:
旋转原理:根据二叉树的特性有: b < a; b > c; b > d; a < e; 由此可得 d < a;旋转90度以后大小关系保持不变因此d节点需要变为a节点的左孩子(b < d < a),而 a 变为b的有孩子(a > b);右旋转同理可得。
先来操作一下我们的红黑树:观察一下发现新插入的节点祖父节点(节点10)为黑色,而父节点和叔叔节点(暂且这么叫吧,毕竟是父亲的兄弟节点)都是红色节点,这种情况直接将父节点叔叔节点都变色就好了:变色完毕是这样的情况:
nice!!!你会发现11和15没有了冲突,但是10和18又遇到了这个问题!!!WHAT?没办法只好继续看了,10的祖父节点为7,这是根节点,显然我们不能让根节点变红啊,如果让根节点变红我们就又多违反了一个性质了不太好。所以这里我们要对这个树进行旋转了,围绕哪里旋转呢?问题在哪就围绕哪!没错就是10-18这条轴,旋转一下得到:
但是问题还是没有解决啊!10-18仍然是两个红色的连在一起的,这个时候我们还需要一次旋转,找哪个轴呢?自然是出了问题的轴,10-18已经旋转过了,所以这里我们只能找下一个,也就是10的上一层,我们围绕7-10轴做一个右旋转可以得到下面的图形:
这一步看上去已经快要差不多了,但是还是违背了性质并且这里还多违背了一个,其实这一步本来应该是做两件事的但是这里分开来看,先是进行旋转操作随后进行变色操作,将旋转轴上面的两个节点进行变色也就是7-10上面的两个都变色,红变黑黑变红,最终结果:
现在检查一下:四个性质还满足不,第一,二很显然满足,第三个也满足,第四个遍历一下发现也是满足的,因此我们通过这些变色和旋转使得这棵树维持了自平衡,great!!!
但是我们前面只是通过观察得到什么时候要变色什么时候要旋转,但是真实的情况可不是这样的代码是不会观察的我们要给他一个确定的情况通知他该干啥,而且我们自己也要弄清楚什么时候该旋转什么时候该变色。这里我们其实分两种情况:第一种,树的分支(我们需要进行处理的分支)为左偏,另一种就是右偏了。这里就说左偏,右偏即可同理得到了。左偏分为三种情况:
1.祖父节点为黑色,且父节点和叔叔节点为红色:
黑色方块中可能有很多子节点我们不关心他因为根据红黑树的性质这些节点中的黑色节点是相等的,遇到这种情况我们需要做的就是将祖父节点与父节点以及叔叔节点变色:
如果下面还是这种情况递归处理变色就好;
第二种:祖父节点为黑色,父节点为红色且叔叔节点为黑色:
这种情况我们进行旋转处理:
这就得到了我们的第三种情况;
第三种情况:
跟上图差不多,这种情况就需要对父节点以及祖父节点进行旋转并且变色的操作,左旋转,并且父节点与祖父节点的颜色都要进行变色处理,处理后的结果如下:
通常处理到这一步都可以完成插入了。树的旋转和变色的时间为O(1)但是变色操作最多会循环㏒(n)次因此红黑树的插入操作时间复杂度为O(㏒(n))。
以上为本人学习的笔记,如果有大佬发现有错请耐心指出,感激不尽!