红黑树 (Red-Black Tree)
本文从搜索二叉树的缺陷出发引出平衡二叉树的概念,紧接着介绍了平衡二叉树的两种实现;最后着重介绍了红黑树的基本概念、特性、原理和常用变化形式,为后续的实现做好基础。
1. BST的缺陷
前文说过,BST是根据链表生成的,于是在某些极端情况下会退化成链表,比如依次添加key为9,8,7,6,5,4,3的结点。这个时候树的优势已经不复存在了,所以为了避免这种问题,提出了改进版本的平衡二叉树。
2. 平衡二叉树
- 所谓平衡二叉树就是抓住了普通BST有可能变成链表的根本原因,并通过附加更多的约束以改善其性能的树结构。
- 那么,普通BST问题的本质是什么呢?
- 其实,其本质在于,后插入的结点无法影响到已经插入的结点,只能在既定树结构的基础之上找到适合自己的位置,这样做的好处是插入过程比较简单,但是坏处也是显而易见的,那就是永远也不会有最优解,因为后面的结点不会影响到之前已经确定位置的结点,所以一旦树从根上就是烂的,那么整个树的性能就不会太高。
- 好的树形结构应该将结点均匀的分布在根结点的两边,这一点不论是对整棵树或者子树都是成立的!
- 平衡二叉树指的就是根结点左右子结点分布均匀的树。
3. 平衡二叉树的种类
- AVL树:缺点是效率比较低
- 红黑树:在插入/删除操作的时候性能要高于AVL树,所以一般都是用红黑树
4. 红黑树之于数据结构学习
红黑树可以看成是界石,红黑树之前算是入门水平。
5. 红黑树特性
-
- 结点分成红色和黑色两种
-
- 根结点必须是黑色
-
- 叶子结点必须是黑色的空结点,记为NIL结点
-
- 每个红色结点的两个子结点都是黑色的;每一条路径中的都不允许两个相邻的红色结点
-
- 【推论】从任何一个结点到其所能到达的叶子结点的所有路径所包含的黑色结点的数目都是**相同的 **
6. 平衡原理
- 为什么具有上述特性的树就是平衡的?其平衡的关键点在什么地方?
- 先说结论:红黑树通过控制路径的长度来控制平衡,具体来说:红黑树中从根结点到叶子结点的最长路径不会超过最短路径的两倍
- 原因:根据推论【从任何一个结点到其所能到达的叶子结点的所有路径所包含的黑色结点的数目都是相同的】,可以知道最短路径中和最长路径中的黑色结点数目相同;所以最长路径中比最短路径长的部分体现在红色结点的差值上;然而根据特性又不允许相邻的两个红色结点,所以任何路径上的红色结点的数目不会超过黑色结点。
- 计算:假设最长路径上的红色和黑色结点数目分别是
lr和lb,最短路径上的红色和黑色结点数目是sr和sb, 并且lb=rb, lr<=lb,于是:(lr+lb)-(sr+sb) = lr-sr < lr <= lb(=sb) <= sr+sb => (lr+lb) < 2*(sr+sb)
7. 红黑树两种变化规则
上文中已经说了,普通BST有缺小的本质原因在于后面插入的结点无法改变之前既定结点;而RBT克服了这个问题,使得后插入的结点也能够影响到既定结点,这里涉及三个基本动作:
-
- 变色
- 新插入一个结点的时候,通常会先假定这个结点是红色的,因为如果是黑色的,则必然会破坏红黑树路径上黑色结点数目相同的设定,而如果是红色的,则有可能一下子成功,而不需要做其他额外的操作;如果尝试插入的红色结点导致红红相连,则需要考虑使用下面的两种变换方法调整树的结构。
-
- 父结点左旋转
- 记父结点为F,F的右子结点为FR,FR的左子结点为FRL
- 变化规则是:FR替换F的位置,F作为FR的左子结点,FR原来的左子结点FRL作为F的右子结点
- FR->F, F->FRL, FRL->FR
--→ FR---
↑ ↓
FRL ←----F
简记:所谓左旋指的就是-> 父结点作为其右子结点的左子结点
-
- 父结点右旋转
- 记父结点为F,F的左子结点为FL,FL的右子结点为FLR
- 变化规则是:FL替换F的位置,F作为FL的右子结点,FL原来的右子结点FLR作为F的左子结点
- FL->F, F->FLR, FLR->FL
--→ FL---
↑ ↓
FLR ←----F
简记:所谓右旋指的就是-> 父结点作为其左子结点的右子结点