简单介绍
什么是红黑树?
红黑树(Red Black Tree)是一种平衡二叉搜索树。红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
关于AVL树可以查看我的博客:juejin.cn/post/713638…
为什么使用红黑树?
红黑树相比AVL树,在检索的时候效率其实差不多,都是通过平衡来二分查找。但对于插入删除等操作效率提高很多。
其次红黑树不像AVL树一样追求绝对的平衡,他允许局部很少的不完全平衡,这样对于效率影响不大,但省去了很多没有必要的调平衡操作,AVL树调平衡有时候代价较大,所以效率不如红黑树。红黑树只是做到了近似平衡,并不严格的平衡,所以在维护的成本上,要比AVL树要低。
红黑树的高度只比高度平衡的AVL树的高度(log2n)仅仅大了一倍,在性能上却好很多。
红黑树的插入、删除、查找各种操作性能都比较稳定。对于工程应用来说,要面对各种异常情况,为了支撑这种工业级的应用,我们更倾向于这种性能稳定的平衡二叉查找树。
红黑树的前身 234树(4阶B树)
它的效率不如红黑树
234树:是一种多叉树,它的每个节点最多有3个数据项和四个子节点。
节点分为3类:
- 2节点:存储1个数据项,2个子节点
3节点:存储2个数据项,3个子节点
- 4节点:存储3个数据项,4个子节点
插入操作
我们将1~10依次插入到234树中,观察如何平衡调整。
首先是1~3的插入,插完以后已经变成一个4节点了。
接着我们插入4,但是234树最多就是4节点,不能直接接在后面了。我们需要进行一步节点上溢操作。上溢中间的节点2,形成一个新的二节点,然后4就接在3后面。接着插入5就直接接在4后面形成4节点了。
接着插入6,由于不能超过4节点又要进行节点上溢了,这里4上溢,然后形成的新树和2要进行合并。
同理插入到8的时候也一样。
最后插入10的时候,将8进行上溢。
上溢并合并以后发现一久大于4节点,于是再将4进行上溢。
最终我们的234树为
234树与红黑树的关联
左边是一棵红黑树,我们将红色节点上移到和他们的父节点同一高度上,实际上就是一棵234树。
所以红黑树和234树是具有等价性的。
红黑树的黑色节点个数=234树的节点数
234树的每一个节点中:
黑色节点必为父节点,红色节点为子节点(黑色中间,红色两边)
那么红黑树的每一种插入情况我们都可以转换为234树来理解。
23树
根据234树的逻辑我们可以完美套用到23树上,只需要知道234树最大是4节点,23树最大是3节点就行了。
红黑树的性质
红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉搜索树的基础上,对于任何有效的红黑树有如下性质:
- 性质1. 结点是红色或黑色。
- 性质2. 根结点是黑色。
- 性质3. 所有叶子都是黑色,且都为空。
- 性质4. 每个红色结点的父节点 and 子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
- 性质5. 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点
234树思维理解红黑树的操作
下面我们采用234树来辅助理解红黑树的插入操作和删除操作。
红黑树的插入操作
如果插入的是根节点,则为黑色。
其余情况插入的节点最开始一定为红色。(如果插入红色节点,仅有一种冲突状况,就是可能出现连续两个红色节点,这时候只需要旋转和变色进行调整。)
红黑树的插入操作分为12种情况:
-
插入节点的父节点为黑色(4种):直接插入,不做调整。因为不会影响到它红黑树的性质。
叔父节点不是红色(4种):变色+旋转。
对于(6)RR型 以234树的思维进行构想,我们会把13加在12的后面,但是红黑树不能有两个一样的红节点,所以我们需要进行变色,将中间的12变成黑色,两边的节点变成红色。
光变色还是不行的,因为黑色节点必须是父节点。我们得进行一次左旋操作。
对于(7)LL型 同理上面的RR型,直接染色加右旋即可。 对于(5)RL型 我们实际上希望11是在10和12中间的,这样的话一次旋转操作是不够的,需要做两次旋转。
首先插入节点染成黑色,然后插入节点的祖父节点也就是10染成红色。
染色到此为止,接下来是旋转操作 首先是父节点12进行一次右旋
然后祖父节点10进行一次左旋。最终到了下图所示的状态,这个是满足红黑树的性质的。
-
对于(8)LR型 和上面的RL型同理,染色然后对父节点进行一次左旋,然后对祖父节点进行一次右旋。
-
叔父节点是红色(4种上溢):变色。
我们依旧用234树的思维来理解,这里无论是哪种情况都是肯定需要上溢的(将4抬高)
然后开始染色,将插入节点的父节点和叔父节点染成黑色。上溢节点4作为新节点染成红色插入到上面一层。
接着由于上溢节点在逻辑上是新插入到上一层的,于是依旧需要再用上面的方式再处理一次4这个节点。从整体上来看就是一种递归的形式。
其他的情况实际上都是一样的,首先将父节点和叔父节点染黑,将祖父节点染红,祖父作为新插入节点上溢,然后递归处理。(写代码的时候需要将4种情况写明,毕竟位置不同。)
红黑树的删除操作
什么是下溢