红黑树(三):插入·续
1. 插入(二):向一棵3-结点树中插入新键
本篇文章续:红黑树(二):旋转和插入
这种情况又可分为三种子情况:新键小于树的两个键,在两者之间,或是大于树中的两个键。每种情况中都会产生一个同时连接到两条红链接的结点,那么我们则需要修复它们。1
教材上长篇大论的分析这三种情况的文字就不Copy啦,图例1结合来讲解吧,更好理解啦哒:
通过上面的图例,我们可以发现,新键最小(case 2)和新键介于两者之间(case 3)可以转化到 新键最大(case 1)下面,也就是case 2 和 case 3可以转化到case 1。这点很重要,后续插入代码中的自平衡操作就是基于这里的观察。
2. 颜色转化(染色)
上面的case 1,我们看到了染色的作用。如果我们遇到一个结点,左右链接都是红色,我们可以把它们变成黑色,将这个结点变成红色,来修复定义2;同时,我们通过染色,将红链接向上进行转递。
那么,染色代码就非常简单啦:
/**
* flip Colors
*
* @param reverse true - set root's color to BLACK,
* children RED ( up-down combing ),
* false - set root's color to RED,
* children BLACK ( bottom-up restoring )
* */
private void flipColors( RedBlackTreeNode root, boolean reverse ) {
root.color = reverse ? BLACK : RED;
( ( RedBlackTreeNode ) root.left ).color = reverse ? RED : BLACK;
( ( RedBlackTreeNode ) root.right ).color = reverse ? RED : BLACK;
}
这里大家先暂时忽略“boolean reverse“这个变量,对于插入,这个值默认为false;true是用来应对删除操作的。
最后再给一个染色的图例1:
3. 根结点的颜色
那么根结点的颜色是什么呢?可红可黑么?答案是否定的,根结点的颜色一定是黑色的。假设我们修复到根结点,发现根结点是红色的,那么按照3-结点的定义,那么根结点之上一定还有一个黑结点,也就是根结点是属于一个3-结点,但是我们知道根结点之上是没有结点了的。所以我们每次再插入后都会将根结点设为黑色。
这也解释了结点定义中的性质2:根节点是黑色,如果你没有学过2-3树,那么对于根结点的颜色,只能解释成一种性质,如果学过2-3树,你就知道我们为什么需要设置根结点为黑色啦。
4. 插入(三):向树底部的3-结点插入新键
现在我们假设需要再树的底部的一个3-结点下加入一个新结点,那么我们再 1. 之中讨论的三种情况都会出现。
指向新结点的链接可能是3-结点的右链接(此时只需颜色转化,case 1),或是左链接(先右旋再染色, case 2),或是中链接(先左旋,然后右旋染色,case 3)
但修复的方法都是一样的,使用左右选和染色进行修复。让我们来看看图例1理解以下吧:
图例中给出了case 3的图解,其他两种情况相对简单,大家可以自己画一遍加深理解哒。
5. 红链接的向上转递
1) 插入(二),中我们说过,无论是case 2还是case 3都可以转化成case 1,而case 1会通过染色将红链接向上传递给父级,之后继续进行自平衡修复。所以这几种变化可以归纳到下面的图示中1:
6. 构造轨迹图例
到此,插入的理论基础已经全部讲解完毕了,接下来,我们将看看如何用代码来实现红黑树的插入 - put()。
上一节:红黑树(二):旋转和插入
下一节:红黑树(四):插入实现
系列汇总:超详细!红黑树详解文章汇总(含代码)
7. 特别感谢
- 感谢 @SENNICHEN 制作系列文章封面图
8. 免责声明
※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;