目录
1. 数据结构树–>树基础
2. 数据结构树–>二叉树
3. 数据结构树–>二叉查找树\二叉排序树
4. 数据结构树–>平衡二叉树
5. 数据结构树–>霍夫曼树
6. 数据结构树–>红黑树
7. 数据结构树–>二叉堆
8. 数据结构树–>B树
9. 数据结构树–>B+树
红黑树
前一篇文章我们了解了平衡二叉树,也知道了平衡二叉树的缺点:大量的插入删除操作影响性能,所以产生了红黑树。
首先:红黑树与平衡二叉树的目的是一样的,都是解决了二叉查找树退化为链表的问题。
平衡二叉树使用的平衡因子,红黑树使用的是几个特性来达到解决二叉查找树退化链表的问题。
1. 红黑树的特性:
- 节点是黑色或者红色。
- 根节点为黑色。
- 每个叶子节点都是黑色(叶子节点是NIL节点)
- 每个红色节点的两个子节点都是黑色(或从根节点到每个叶子节点的路径上不能有两个连续的红色节点)。
- 从任一节点到其每个叶子节点的所有路径上都包含相同数目的黑色节点。
注意:红黑树的叶子节点指的是 NIL节点,也就是普通树叶子节点的孩子节点。
红黑树图(抄图)
红黑树没有平衡因子与平衡二叉树没有关系,红黑树的几个特性保证了红黑树从根节点到叶子节点的最长路径不会超过最短路径的2倍。
当红黑树进行插入或者删除时可能破坏红黑树的特性,这就需要我们做出调整来维持红黑树的特性。
2. 红黑树的变色与旋转
2.1 变色
这个好理解,红黑树只有两种颜色,不是黑就红,这里为满足红黑树特性, 当红黑树不满足自平衡时,我们可以通过节点的变色来满足红黑树的自平衡。
2.2 旋转
二叉树的旋转之前我们在平衡二叉树插入删除时已经详细介绍了什么是左旋与右旋,红黑树的左旋与右旋操作与平衡二叉树是一致的,通过旋转来满足红黑树的自平衡。
2.3 总结
红黑树自平衡又变色,有旋转,但什么时候该旋转,什么手该变色呢?红黑树的特性有5点,当我们做插入删除操作时需要判断多种条件来满足自平衡,我们后续会
一步一步详细介绍,前人的基础为我们提供了详细的逻辑与步骤,不用怕麻烦,我们只是把逻辑的代码实现而已。
3. 红黑树的插入
3.1 插入
红黑树的插入操作是和二叉排序树一致的,根据二叉排序树特性,找到插入位置插入节点。
3.2 插入节点变红
根据红黑树的特性与前人的总结,新插入的节点都是红色,这样一般只会破坏特定4,需要修复的不平衡也相比较简单。我们以新插入的节点为判断开始节点,开始判断红黑树的平衡。
3.3 插入后的自平衡
上图是盗图,清楚明白的标注了插入节点的各种情况需要我们怎么做。
推荐阅读:
红黑树添加和删除节点原理
插入示例:通过插入12 1 9 2 0 11 7 19 4 15 18 5 14 13 10 16 6 3 8 17完成上述所有情形的展示。
3.3.1 插入12
普通情况: 初始化插入节点为根节点直接涂黑。
3.3.2 插入1
普通情况: 插入节点的父节点为黑色,直接插入,无需自平衡。
3.3.3 插入9
插入完成后节点9变红色,红黑树不平衡:
情况2: 父节点红色父节点是祖父节点的左支:叔叔节点为黑色,插入节点是父节点的右支------> 父节点左旋—>父节点1变为当前节点—>
父节点1变为当前节点—>情况1---->父节点与祖父节点颜色互换,祖父节点右旋---->红黑树平衡达到。
3.3.4 插入2
插入后属于情况3:父节点红色,父节点是祖父节点的左支, 叔叔节点红色------>情况3------> 父节点与叔叔节点变黑,祖父节点变红---->祖父节点变为当前处理节点
------>普通情况:重新判断当前处理节点为根节点—>当前处理节点变黑---->红黑树平衡。
3.3.5 插入0
普通情况无需调整。
3.3.6 插入11
普通情况无需调整
3.3.7 插入7
插入后属于情况6:父节点红色,父节点是祖父节点的右支,叔叔节点为红色----> 父节点与叔叔节点变黑,祖父节点变红,祖父节点变为当前处理节点—>判断祖父节点
---->普通情况无需调整—>红黑树平衡。
3.3.8 插入19
普通情况无需调整。
3.3.9 插入4
插入后属于情况4:父节点红色,父节点是祖父节点右支,叔叔节点黑色,插入节点是父节点的左支。
情况4------> 父节点右旋,当前处理节点变为旋转前,前一个处理节点的父节点7------>判断节点7情况5------> 祖父节点颜色互换,祖父节点左旋------>平衡完成。
3.3.10 插入15
插入后属于情况6----->处理完成,祖父节点变为处理节点---->判断为普通情况无需调整---->红黑树平衡完成
3.3.11 插入18
插入后判断为情况2---->父节点左旋,处理节点变为节点15------>判断为情况1—>父节点与祖父节点颜色互换,祖父节点右旋----->红黑树平衡。
3.3.12 插入5
插入后判断节点5,处于情况6---->处理,当前处理节点变为节点4------>判断情况3----->处理,当前节点变为根节点9---->判断情况普通情况---->根节点直接涂黑---->红黑树平衡。
3.3.13 插入14
判断插入节点14为情况3------>处理—>当前处理节点变为节点18—>判断节点18情况为普通情况无需操作—>红黑树平衡。
3.3.14 插入13
插入节点13判断为情况1—>父节点与祖父节点颜色交换,祖父节点右旋—>红黑树平衡
3.3.15 插入10
普通情况无需操作
3.3.16 插入16
判断节点16为情况6------>处理,节点14变为处理节点---->判断节点14为情况4------>处理,节点18变为处理节点---->判断节点18为情况5---->处理达到红黑树平衡。
3.3.17 插入6
判断插入节点6为情况2 ------>处理,节点5变为当前处理节点------->判断节点5为情况1------>处理----->红黑树平衡
3.3.18 插入3
普通情况无需操作。
3.3.19 插入8
判断插入节点8为情况6---->处理,祖父节6点变为当前处理节点----->判断节点6为情况5----->处理----->红黑树平衡。
3.3.20 插入17
判断插入节点17为情况5—>处理---->红黑树平衡。
3.4 代码的实现
红黑树插入代码与插入后的平衡其实最好的就是看java源码吗,java中 TreeMap就是最好的例子。
//TreeMap 红黑树平衡代码,简洁高效
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}
4. 红黑树的删除
红黑树的节点删除逻辑与二叉排序树的删除逻辑是一样的,不同的是怎么才能保证红黑树的自平衡。
4.1 确认开始平衡的节点。
从上面的逻辑图可以看出,开始平衡的节点重点在于替换节点的是否有左或右孩子,如果有则删除后孩子节点为开始平衡的节点,如果没有则自己为
开始平衡的节点,平衡后删除自己。
4.2 红黑树删除
红黑树删除和插入一样都遵循一定的逻辑,只要我们找到了开始平衡的节点,按照逻辑就能很快重新平衡二叉树
红黑树删除的平衡逻辑
我们使用插入结束后的红黑树来完成删除的讲解。
4.2.1 删除12
- 判断节点12,有左右子树,则开始查找替换节点,以二叉查找树的删除逻辑,我们查到替换节点为节点13。我们将节点13的数据赋值给节点12,替换节点13重新变为了要删除的节点,同时也能确定替换节点变为了平衡开始节点。
- 判断黑色节点13(替换节点)属于情况4------>处理:父节点与兄弟节点颜色交换,兄左子节点涂黑,入接点右旋---->平衡结束。
- 删除多余的替换节点。
4.2.2 删除1
和插入一样,每删除一个都需要条件判断,来完成红黑树平衡,这里不再一步一步尝试。