欢迎探讨,若有错误敬请指正html
如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 编程
相关博客:编程语言
2-3-4树和红黑树是彻底等价的,因为绝大多数编程语言直接实现2-3-4树会很是繁琐,因此通常是经过实现红黑树来实现替代2-3-4树,而红黑树本也一样保证在O(lgn)的时间内完成查找、插入和删除操做。blog
红黑树是每一个节点都带有颜色属性的平衡二叉查找树 ,颜色为红色或黑色。除了二叉查找树通常要求之外,对于任何有效的红黑树咱们增长了以下的额外要求:get
(1) 节点是要么红色或要么是黑色。博客
(2) 根必定是黑色节点。it
(3) 每一个叶子结点都带有两个空的黑色结点(称之为NIL节点,它又被称为黑哨兵)。效率
(4) 每一个红色节点的两个子节点都是黑色(或者说从每一个叶子到根的全部路径上不能有两个连续的红色节点)。
(5) 从任一节点到它所能到达得叶子节点的全部简单路径都包含相同数目的黑色节点。
这些性质保证了根节点到任意叶子节点的路径长度,最多相差一半(由于路径上的黑色节点相等,差异只是不能相邻的红色节点个数),因此红黑树是一个基本平衡的二叉搜索树,它没有AVL树那么绝对平衡,可是一样的关键字组成的红黑树相比AVL旋转操做要少,并且删除操做也比AVL树效率更高,实际应用效果也比AVL树更出众。固然红黑树的具体实现也复杂的多。
红黑树的这5个性质中,第3点是比较难理解的,但它却很是有必要。咱们看上面这张图,若是不使用黑哨兵,它彻底知足红黑树性质,根结点5到两个叶结点1和叶结点9路径上的黑色结点数都为3个,且没有连续红色节点。
但若是加入黑哨兵后,叶结点的个数变为8个黑哨兵,根结点5到这8个叶结点路径上的黑高度就不同了,因此它并非一棵红黑树。NIL节点的存在还可使得红黑树在代码实现方面获得简化,在具体实现过程当中咱们只须要1个NIL节点便可,详情请关注本博客的下一篇文章,有关红黑树代码的实现。
红黑树的全部性质其实均可以从2-3-4树来理解,这也是理解红黑树最好的方式,由于红黑树本质就是2-3-4树。
若是一棵树知足红黑树,把红结点收缩到其父结点,就变成了2-3-4树,全部红色节点都与其父节点构成3或4节点,其它节点为2节点。图中NIL节点未画出。
因此红黑树的每一类型操做都与2-3-4树一一对应。黑色节点的个数(或者说位置)对应2-3-4树中的节点个数(或者说位置),这样能够很好的理解性质3(从每一个叶子到根的全部路径上不能有两个连续的红色节点)和性质5(从任一节点到它所能到达得叶子节点的全部简单路径都包含相同数目的黑色节点)以及根节点到任意叶子节点的路径长度,最多相差一半。
同时咱们还须要明白的是,一颗红黑树对应惟一形态的2-3-4树,可是一颗2-3-4树能够对应多种形态的红黑树(主要是3节点能够对应两种不一样的红黑树形态),上图中的2-3-4树还能够对应下图中的红黑树。咱们在后面红黑树的删除操做中会利用这种状况。
由于每种书中对旋转的定义不一致,因此咱们有必要在这里特此说明一下。以某一个节点为轴,它的左子枝顺时针旋转,做为新子树的根,咱们称之为顺时针旋转(clockwise)或者右旋转。同理,以某一个节点为轴,它的右子枝逆针旋转,做为新子树的根,咱们称之为逆时针旋转(anticlockwise)或者左旋转。
(1)若是红黑树中已存在待插入的值,那么插入操做失败,不然必定是在叶子节点进行插入操做,执行步骤2。
(2)当咱们插入一个新节点后,咱们会把该节点涂红(涂红操做,从2-3-4树的的角度看来,就是向上层节点进位一个key),因为插入操做可能破坏了红黑树的平衡性,因此咱们须要不断回溯,进行调整。调整过程就是颜色变换和旋转操做,而这些操做均可以从2-3-4树来理解。考虑到回溯的状况,从2-3-4树的角度,咱们能够把X节点当作向上层进位的key。
插入新节点时,咱们可能会遇到如下几种状况
插入后直接涂红,若是父亲节点是个黑色,插入结束。
绿色箭头表示插入的位置,上图中的虚线表示能够有该节点,也能够没有该节点,若是有,必定是红色。固然还有可能在对称的状况,即在右子支插入,操做方式都是同样的,因为不涉及到旋转操做,因此代码的实现方式也同样,不在赘述。
这个操做能够从2-3-4树来理解,至关于2-3-4树中待插入的叶子节点是个2节点(对应黑父没有孩子节点)或者3节点(黑父有孩子节点,孩子节点的颜色必定是红色)。在回溯调整的过程当中也会遇到这个状况,回溯时X表示的是下一层向上进位的key,到这个时候就不须要继续回溯了。
这种状况还有对应的镜像状况,即P为G的右子支状况
这种状况不会在叶子节点出现,可是会出如今回溯调整的过程当中。这种状况至关于2-3-4树中,容纳进位的父节点为3节点,还有空间能够容纳key,因此到此就不用继续回溯了。
这种状况至关于2-3-4树中,向上进位的父节点为4节点,因此先分裂(对应P和B的颜色变换)而后再插入X,而后继续回溯,把G当作向更上一层进位的节点(即把G当作新的X)。
这种状况还有对应的镜像状况,即P为G的右子支状况,但在具体的代码实现过程当中,由于不涉及到旋转操做,因此不用区分。
删除操做能够归纳为如下几个步骤:
(1)查找要删除的值所在的节点,若是不存在,删除失败,不然执行步骤2
(2)若是要删除的节点不是叶子节点,用要删除节点的后继节点替换(只进行数据替换便可,颜色不变,此时也不须要调整结构),而后删除后继节点。
那么真正须要删除的节点有如下几种可能性
这时,咱们删除这个黑色节点后须要进行调整,在图中X总表示下一层的节点,一开始X表示NIL节点(回溯过程当中X会不断向上层迭代)。须要调整的状况又能够分为如下几种。
这种状况还有对应的镜像状况,即P为G的右子支状况
上述两种状况大体对应2-3-4树删除操做中兄弟节点为3节点或4节点,父节点key下移,兄弟节点key上移动,但不彻底一致。
上述两种状况都对应2-3-4树删除操做中兄弟节点为2节点,父节点至少是个3节点,父节点key下移与兄弟节点合并。
对应2-3-4树删除操做中兄弟节点为2节点,父亲节点也为2节点,父节点key下移与兄弟节点合并,已父节点当作新的X,继续回溯。
按照2-3-4删除操做的原理,咱们这里应该检测黑侄R(第二幅图中是L)的两个孩子节点是否存在红色节点(对应2-3-4树,是不是2节点),但这样作使用的局部变量也会增多,代码实现起来也会变得很是复杂。咱们这里作了一个技巧性处理,以P为轴进行旋转,它原理就是第2部分2-3-4树和红黑树的等价关系中讲到的:一颗2-3-4对应的红黑树形态并不惟一。
上面的两种红兄状况,旋转后对应的仍是同一颗2-3-4树(只是B和P组成的3节点在红黑树的两种不一样形态而已),但此时X的兄弟节点和侄子节点发生变化,如今X的兄弟节点就变成了R(第二幅图中是L),咱们正需检查要R(第二幅图中是L)的两个孩子节点的颜色。实际上,此时咱们又回到上面讨论过的了黑兄的状况。