从标准二叉树的极端状况咱们推导出2-3树这样的数据结构具有自平衡的特性,可是要实现这个特性在算法上至关复杂。考虑在大部分状况下,对于检索的指数级时间消费O(lgN)要求并不严格。所以,咱们会看到如何将一颗标准的2-3树转变成红黑树的过程。算法
1、局部变换数据结构
考虑若是在2-节点上挂新的键并不会破坏2-3树的平衡结构。但是在3-节点上挂新的键,可能的变化却多达6种。这个临时的4-节点多是根节点,多是一个2-节点的左子节点或者右子节点,也多是3-节点的左子节点、中子节点或者右子节点。2-3树插入算法的根本在于这些变换都是局部的:除了相关的节点和连接以外没必要修改该或者检查树的其余部分。所以,每次变换中,变动的连接数量都不会超过一个很小的常熟。this
每个变换都会将4-节点中的一个键送入它的父节点中,并重构相应的连接,但没必要涉及树的其余部分。这也是2-3树的重要特性:一次聚聚变换不会影响树的有序和平衡性:spa
2、红黑树与2-3树的相互转换3d
尽管2-3树一种高效的结构并提供了自平衡特性,可是它的算法过于复杂以致于在通常的状况下咱们并不肯意使用这样的结构做为首选方案。可是红黑树则否则,它的实现算法很是简单,代码量也不大。但理解整个红黑树的构造过程却须要一番仔细的探究。code
红黑二叉查找树背后的基本思想是用标准的二叉查找树和一些额外信息来表示2-3树。咱们将树中的连接分为两种类型:红链接将2个2-节点链接起来构成一个3-节点,黑连接则是2-3树中的普通连接。对象
对于红黑树咱们给出的定义以下:blog
1.红链接均为左链接;io
2.没有任何一个节点同时和两条红链接相连;class
3.该树是完美黑色平衡的,即任意空节点到根节点的路径上的黑色连接数量相等。
咱们只须要将一颗红黑树中的红链接画平,那么全部的空节点到根节点的距离都是相同的。若是咱们将由红链接项链的节点合并,获得的就是一颗2-3树。
方便起见,由于每个节点都只会由一条指向本身的连接,咱们将连接的颜色泊岸村在表示节点的Node对象中,布尔变量color为true表示红色,false为黑色。节点定义代码以下:
class Node { private Key key; private Value val; private Node left; private Node right; private boolean color; private int size; public Node(Key key, Value val, boolean color, int size) { this.key = key; this.val = val; this.color = color; this.size = size; } }
3、旋转和颜色变化
对红黑树来讲旋转是它的基础变化,在实现代码前咱们先思考一下为何须要旋转。
向2-3树的2-节点插入新的键是很是简单的,不管这个键自己是E仍是S,在插入新的键之后都一个样子。但对于红黑树来讲就不是这样了,回想一下以前对红黑树的定义:红链接均为左链接。若是要插入的新值小于节点咱们能够直接插入,若是新值大于节点,咱们就会建立一条红色的右连接。所以咱们就须要经过旋转将右连接变成左链接。固然更复杂的状况下,咱们还会经过旋转把左链接变成右连接。也许到这里有人会问:当插入的新键大于节点的时候,为何不能够直接插入一条黑色的右连接呢?在思考一下2-3树的生长特性以及红黑树对平衡的定义:
1.2-3树是区别于二叉树,它是向上生长的。
2.红黑树中任一空节点到根节点的距离相同。
综合起来就是,咱们在红黑树中插入的新键都必须从红连接开始,再经过变换来调整树的高度以恢复平衡。
接下来咱们再考虑一个问题,若是咱们向红黑树插入了一条向左的红连接(显然此时并未破坏树的平衡性,所以咱们没必要调整树的结构)而后再向同一个节点插入了一条红色的右连接怎么办?左连接已经被占据了,咱们没法再经过旋转调整平衡。
回想一下2-3树在遇到这样的状况是是如何变化的:咱们先构造一个临时的4-节点,再将它转换成3个2-节点并把父节点向上转移。那么对于红黑树来讲彷佛就更简单了一些。咱们只须要调整两条子连接为黑连接并把根连接变成红链接便可。
你或许又会问了:若是父节点已是红色的怎么办呢?事实上你彻底不用担忧这样的状况发生,由于红黑树的另外一条特性已经保证了:没有任何一个节点同时和两条红链接相连。换言之,若是父节点(E)为红色,当咱们在插入(A)的时候已经不符合红黑树的定义,咱们须要经过旋转来恢复平衡。
总的来讲,不管以前节点的状况如何,咱们经过0次,1次或2次旋转以及颜色的变化获得指望的结果。