红黑树
目标:
- 2-3树
- 红黑树
- 添加元素
- 性能测试
- 其他话题
1.1 2-3树
-
特点
-
满足二分搜索树的基本性质
-
节点可以存放一个元素或者两个元素
3节点理解:左节点元素比b小,中间节点位于b和c之间,右节点大于c。
-
每个节点有2个或者3个孩子——2-3树
-
2-3树是一棵绝对平衡的树
-
-
绝对平衡
-
在2-3树中添加元素,绝对不会添加到空节点的位置上去 ,只会跟叶子节点做融合
- 插入3-节点
-
插入3-节点 父亲节点是2-节点
-
插入3-节点 父亲节点是3-节点
-
-
红黑树和2-3树的等价性
引入红色节点,表示的意思是,和它的父亲节点一起表示2-3树中的3节点。在红黑树中,所有的红色节点都是左倾斜的。那么之前的2-3树,转换为红黑树即为:
对于任意一棵2-3树,都可以按照这种等价规则转化为红黑树。
1.2 红黑树
-
基本性质(算法导论)
- 每个节点或者是红色的,或者是黑色的
- 根节点是黑色的(特殊:空树)
- 每一个叶子节点(最后的空节点)是黑色的
- 如果一个节点是红色的,那么他的孩子节点都是黑色的
- 从任意一个节点到叶子节点,经过的黑色节点都是一样的
-
复杂度分析
- 红黑树是保持“黑平衡”的二叉树,严格意义上,不是平衡二叉树
- 最大高度:2logn,但是复杂度依旧是O(logn)
- 不会像二分搜索树一样退化为链表,其增删查改的操作都是O(logn)。
- 如果添加、删除元素频繁,那么相比于AVL来说,红黑树会更好。但如果更侧重于查询来说,AVL可能会更好。
-
添加元素
正常面试中对于红黑树,一般来说知道原理即可,很少有手推导的,这是因为,红黑树的添加元素等操作,不是一般的难……而且,考察不了对数据结构真正的理解和认识。
-
2-3树中添加元素
-
或者添加进2-节点,形成一个3-节点
-
或者添加进3-节点,暂时形成一个4-节点
-
永远添加红色节点
-
LL
但是,当一棵空树添加元素,需要保持根节点为黑色。当在根节点左侧添加元素(小于根节点),此时符合要求。不过当在根节点右侧添加元素,例如根节点为37,添加42,那么此时42为红色,而我们知道红色节点必须在黑色节点的左侧,所以我们需要进行左旋转的操作。如下:
左旋转代码如下:
// 红黑树中左旋转的过程 // node x // / \ 左旋转 / \ // T1 x ---------> node T3 // / \ / \ // T2 T3 T1 T2 private Node leftRotate(Node node){ Node x = node.right; // 左旋转 node.right = x.left; x.left = node; x.color = node.color; node.color = RED; return x; }-
颜色翻转
比如当根节点为42,此时添加左节点37和右节点66,那么颜色变化为
结合上面2-3树中,插入3节点示意图看出,临时4-节点变为3个2-节点的子树,也就意味着这3个2-节点都是黑色的节点,代表单独的2-节点。此时只需要变为黑色节点即可。然后,根节点42,需要继续向上与父节点进行融合,融合的过程也就是42变为红色。颜色变化为:
实现代码如下:
// 颜色翻转 private void flipColors(Node node){ node.color = RED; node.left.color = BLACK; node.right.color = BLACK; } -
RR
举例说明:此时树结构为根节点42,左子节点为37,现在新添加元素12,颜色为:
那么此时,需要进行右旋转,旋转完后,颜色变换如下:
此时,37作为根节点,继续向上融合,然后再进行上面说的颜色变换。37变为红色,12和42变为黑色。右旋转代码如下:
// 红黑树右旋转的过程 // node x // / \ 右旋转 / \ // x T2 -------> y node // / \ / \ // y T1 T1 T2 private Node rightRotate(Node node){ Node x = node.left; // 右旋转 node.left = x.right; x.right = node; x.color = node.color; node.color = RED; return x; } -
LR
举例说明:根节点为42,左子节点为37,现在添加元素40
那么此时需要先左旋转
然后进行右旋转,再转换颜色
-
汇总:
汇总以上情况可以总结如下图:
维护的时机与AVL树一样,添加节点后回溯向上维护。
-
-
1.3 性能测试
- 对于完全随机的数据,普通的二分搜索树就很好用
- 缺点:极端情况退化为链表(或者高度不平衡)
- 对于查询较多的使用情况,AVL树很好用
- get
- contains
- set
- 红黑树牺牲了平衡性(2logn的高度)
- 统计性能更优(综合增删改查所有的操作)
- Java.util中的TreeMap、TreeSet基于红黑树
1.4 其他话题
- 删除节点
- 右倾红黑树
- Splay Tree(伸展树)
- 局部性原理:刚被访问的内容下次高概率被再次访问
- 算法导论中红黑树的实现