Java 中的 TreeMap,JDK 1.8 中的 HashMap、C++ STL 中的 map 均是基于红黑树结构实现的
红黑树是一种特殊的二叉搜索树,是自平衡的二叉搜索树(说法不是很准确)
红黑树是实现,自平衡的二叉搜索树是算法和逻辑
叶节点的层级差的绝对值不超过1,叶子表示nil值。
否则,需要旋转(插入时导致不平衡)
学习红黑树前,先学2-3-4树
2-3-4树
概念
在普通的二叉查找树上进行了拓展,它允许有多个键(1到3个),且树保持完美平衡(根到每个叶子结点的路径一样长)。
每个结点可以拥有1,2或者3个键
2-node: 1个键,两个孩子
3-node: 2个键,三个孩子
4-node: 3个键,四个孩子
如图:
插入
查找键应该插入的位置(树底,因为要完美平衡)
2-node:转换成3-node
3-node:转换成4-node
4-node:怎么办?
分裂
![](https://i-blog.csdnimg.cn/blog_migrate/3d7a07e6d31577cddab61c6b7608dd69.png)
Bottom - up 自底向上
top - down 自顶向下(更好,遇到问题及时处理,而非回头再处理)
遇到4结点,先向上分裂再向下插入
这些变换都是局部变换,不会影响到无关的其他节点
性能分析
最后(学习红黑树)
学习的是第三版即最常用的左倾红黑树LLRB Trees(Left-leaning Red Black Tree)
怎么把一个2-3-4树转换成一个红黑树?
用内部的红色的边来表示3 - node和4 - node(黑色的边在2-3-4树中原本就是黑色的,红色的边是2-3-4树中的3结点和4结点分裂得来的)(红边相连接的表示原本在一个节点中)
注意:一般红黑指结点,这里红黑指边是一个道理(将边的入点看做对应边的红黑就可以)
具体:把2-3-4树中的3、4结点分裂,把下移的结点和上移的结点的中间的边变为红色。
2-3-4树能够被表示成BST,它们之间有一种对应关系!但是它们这种对应不是1-1的。(如下有4种情况)
改进版的定义:即左倾红黑树的定义:在2-3-4树的3结点分裂的时候,左元素下移,右元素上移。(如下只有情况1符合定义)
算法导论中的红黑树定义
注意定义中的4和5(黑高平衡)
分割线(以上为Lesson内容,以下为自己整理内容!)
红黑树概念
普通的二叉查找树在极端情况下可退化成链表,此时的增删查效率都会比较低下。为了避免这种情况,就出现了一些自平衡的查找树,比如 AVL,红黑树等
这些自平衡的查找树通过定义一些性质,将任意节点的左右子树高度差控制在规定范围内,以达到平衡状态。
以红黑树为例,红黑树通过如下的性质定义实现自平衡:
- 节点是红色或黑色。
- 根是黑色。
- 所有叶子都是黑色(叶子是NIL节点)。
- 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点(简称黑高)。
理解:红黑树的定义解决两个问题
1、避免二叉查找树退化成单链表的情况
2、考虑某个结点到其每个叶子结点的路径长度问题
性质4和性质5。可保证任意节点到其每个叶子节点路径最长不会超过最短路径的2倍
当某条路径最短时,这条路径必然都是由黑色节点构成
在路径最长的情况下,路径上红色节点数量 = 黑色节点数量
以上可以得出:该路径长度为两倍黑色节点数量,也就是最短路径长度的2倍
操作
旋转操作
左旋是将某个节点旋转为其右孩子的左孩子,右旋是节点旋转为其左孩子的右孩子。
左旋和右旋:
右旋:(具体步骤)
插入
红黑树的插入过程和二叉查找树插入过程基本类似,不同的地方在于,红黑树插入新节点后,需要进行调整,以满足红黑树的性质。
性质1规定红黑树节点的颜色要么是红色要么是黑色,那么在插入新节点时,这个节点应该是红色还是黑色呢?
如果插入的节点是红色,此时所有路径上的黑色节点数量不变,仅可能会出现两个连续的红色节点的情况。这种情况下,通过变色和旋转进行调整即可,比之前的简单多了。
分析插入红色节点后红黑树的情况。这里假设要插入的节点为 N,N 的父节点为 P,祖父节点为 G,叔叔节点为 U
插入红色结点后,出现以下5种情况:
情况1:插入的是根结点
插入的新结点是红黑树的根结点
情况2:N的父结点为黑色
情况3:父结点是红色,叔叔结点也是红色
N 的父节点是红色(节点 P 为红色,其父节点必然为黑色),叔叔节点 U 也是红色。由于 P 和 N 均为红色,所有性质4被打破,此时需要进行调整。
先将 P 和 U 的颜色染成黑色,再将 G 的颜色染成红色
情况4:父结点是红色,叔叔结点是黑色
对结点P进行左旋,转化为情况5,恢复性质4
情况5:父结点是红色,叔叔结点是黑色(情况4转化而来)
右旋(结点旋转为其左孩子的右孩子):调整P和G的位置,并互换颜色。(性质4恢复,未破坏性质5)
插入总结:
情况1和2比较简单
情况3、4、5稍复杂,区别在于叔叔结点的颜色
如果叔叔节点为红色,直接变色即可。如果叔叔节点为黑色,则需要选选择,再交换颜色。(重点!重点!重点!)
把三种情况的图画在一起就容易观察了:
删除(较插入更为复杂)
删除操作首先要确定待删除节点有几个孩子,如果有两个孩子,不能直接删除该节点。而是要先找到该节点的前驱(该节点左子树中最大的节点)或者后继(该节点右子树中最小的节点),然后将前驱或者后继的值复制到要删除的节点中,最后再将前驱或后继删除。
红黑树删除操作的复杂度在于删除节点的颜色
假设最终被删除的节点为X(至多只有一个孩子节点),其孩子节点为N,X的兄弟节点为S,S的左节点为 SL,右节点为 SR。
情况1:删除根结点
N 是新的根。在这种情形下,我们就做完了。我们从所有路径去除了一个黑色节点,而新根是黑色的,所以性质都保持着。
上面是维基百科中关于红黑树删除的情况一说明
可能还有其他情形
情况2:删除根结点
情况3:删除根结点
把S染成红色
所有经过 S 的路径比之前少了一个黑色节点,这样经过 N 的路径和经过 S 的路径黑色节点数量一致了
情况4:删除根结点
情况5:删除根结点
情况6:删除根结点
删除总结:
把所有情况对应的图形画在一起时,拨云见日,一切都明了了
总结
本文对红黑树的分析也主要参考了维基百科中的红黑树分析