1、红黑树简介
红黑树(Red Black Tree)是一种重要的数据结构,大多数自平衡BST(self-balancing BST) 库函数都是用红黑树实现的,比如C++中的map 和 set (或者 Java 中的 TreeSet 和 TreeMap)。红黑树也用于实现 Linux 操作系统的 CPU 调度。完全公平调度(Completely Fair Scheduler)使用的就是红黑树
2、红黑树与AVL树(二叉平衡树)的比较:
-
AVL树的时间复杂度虽然优于红黑树,但是对于现在的计算机的cpu速度,可以忽略性能差异
-
红黑树的插入删除比AVL树更便于控制操作
-
红黑树旋转情况少于AVL树
使用建议:AVL 树比红黑树更加平衡,但AVL树在插入和删除的时候也会存在大量的旋转操作。当涉及到频繁的插入和删除操作,应选择性能更好的红黑树。
3、红黑树规则
一个节点上记录着:key值,value值,颜色,左节点,右节点,父节点信息;左子树上所有节点的值都小于根节点的值,右子树上所有节点的值都大于根节点的值(左小右大),红黑树规则如下:
-
每个节点或者是黑色,或者是红色。
-
根节点是黑色。
-
每个叶子节点(NIL)是黑色。 (注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!)
-
如果一个节点是红色的,则它的子节点必须是黑色的。(没有连续的红节点)
-
从一个节点到其子孙节点的所有路径上包含相同数目的黑节点。
【最长路径是最短路径的两倍:最短路径就是全黑节点,最长路径就是一个红节点一个黑节点交替出现】
4、红黑树的左旋和右旋
左旋:
(A是操作节点,B是A的左子树,C是A的右子树)
-
当操作的结点是整棵树的根节点,那么左旋实现为下面步骤:
a. 将结点C的左子树变化为A的右子树
b.将结点A的父结点指针parent指向结点C
c.将C结点的父结点指针parent置为NULL。
-
当操作的结点有父结点,那么左旋实现为下面步骤:
a. 将结点C的左子树变化为A的右子树
b.将结点A的父结点指针parent指向结点C
c.将C结点的父结点指针parent置为原来A的父指针的指向,同时也让原来A的父节点指向C。
右旋:
和左旋一样,就是换个方向
【参考:红黑树的左旋、右旋和颜色变换 - hxhlrq - 博客园
5、红黑树插入节点
-
如果插入前是空树,那么新插入的元素就会成为根节点颜色为黑色
-
如果红黑树非空,那么在红黑树中插入新的结点时,所有的点都默认是红色结点
当插入一个节点时,默认将节点设置为红色,为什么呢?由于红黑树要求,从每一个节点出发到其后代的叶子结点的任一路径上的黑色节点个数相等,如果插入黑色节点,就会导致某条路径上的黑色节点数量+1,此时调整起来是非常麻烦的。如果插入的节点是红色,当父节点是黑色时,直接插入即可;当父节点是红色时,才需要分类讨论插入时的调整情况,因此设置插入节点的默认颜色为红色。
当父节点为红色时,有以下几种情况:
(此时新插入的节点为红色,父节点也为红色,由于父子节点不可能都红,祖父节点必为黑色,所以只需要讨论叔父节点的颜色即可)
叔父节点为红色:
-
把父结点、叔父节点设为黑色
-
把祖父结点设置为红色
-
如果发现祖父节点的颜色和上面的颜色冲突,按照前两步继续变色
叔父节点为黑色:
-
父节点为祖父节点的左子树:右旋
-
父节点为祖父节点的左子树:左旋
-
根节点必黑,新增是红色,只能黑连黑,不能红连红
-
叔红就变色,叔黑就旋转,哪边黑往哪边转