红黑树

作者: shark没有辣椒 | 来源:发表于2021-04-18 17:48 被阅读0次

    要学习红黑树,首先我们要先了解二叉查找树,二叉查找树有如下特性

    1.左子树上所有结点的值均小于它的根结点的值。
    2.右子树上所有结点的值均大于它的根结点的值。
    3.左、右子树也分别为二叉查找树。

    下面我们来看一个典型的二叉查找树,查找值为10的节点。


    图1

    首先根节点9小于10,所以查看右子树节点13。


    图2

    节点13大于10,所以我们再查看左子树节点11。


    图3

    由于11大于10,所以我们再查看左子树节点,结果成功查找到值为10的节点。


    图4

    这种方式正是二分查找的思想,查找所需的最大次数等同于二叉查找树的高度。在插入节点的时候,也是采用类似的方法,通过一层一层比较大小,找到新节点适合插入的位置。但是插入或者删除后要保持树的平衡,是一件麻烦事。比如图5初始的二叉查找树只有三个节点,根节点值为9,左孩子值为8,右孩子值为12;我们依次插入如下五个节点:7,6,5,4,3。由每次插入的节点都比最小值要小,最终所有的节点就连成了一条链,如图6所示,此时深度为n,查找的时间复杂度就是O(n)了。


    图5
    图6

    为了解决这个问题,有人发明了平衡二叉树,又被称为AVL树,具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是,平衡二叉树也有一定的局限。对于平衡二叉树来说,每个节点的子树的高度差不能超过1,一旦出现违反的情况,就需要进行左旋或者右旋操作,从而调整二叉树的高度。但是,在插入和删除节点的过程中,平衡度超过1是经常发生的事情,这也就导致了调整操作频繁地发生,从而提高时间复杂度。也就是说,这种追求绝对平衡的行为,并不是一个好的做法。

    这就引出了我们今天的主角红黑树(RB_Tree),红黑树是一种自平衡的二叉查找树。除了符合二叉查找树的基本特性外,它还需要满足如下规则:

    1.节点是红色或黑色。
    2.根节点是黑色。
    3.每个叶子节点都是黑色的空节点(NIL节点)。
    4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
    5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

    下面这棵树,就是一个典型的红黑树


    图7

    这五条规则单独分开来看都很好理解,但是放在一起就有点迷惑了,为什么是这五规则,它们之间有什么关系?其实这些复杂的定义和规则都是为了保证树的平衡性,因为保证树的平衡性才能降低树的高度。树的查找性能取决于树的高度,所以树的高度越低搜索的效率越高!这也是为什么存在二叉树、搜索二叉树等,各类树的原因。

    当插入或删除节点的时候,红黑树的规则可能被打破,这时候需要我们进行调整来维持这些规则。下面举个例子。
    我们向刚才图5的红黑树插入值为14的新节点。

    图8

    再插入值为21的节点。


    图9

    由于父节点22是红色节点,因此这种情况打破了红黑树的规则4(每个红色节点的两个子节点都是黑色),必须进行调整,使之重新符合红黑树的规则。

    那需要怎么调整呢?一般有两种方法,变色和旋转,而旋转又分为两种形式,左旋转和右旋转。

    首先来看变色
    为了重新符合红黑树的规则,尝试把红色节点变为黑色,或者把黑色节点变为红色。
    下图所表示的是刚才红黑树的一部分,需要注意节点25并非根节点。因为节点21和节点22连续出现了红色,不符合规则4,所以把节点22从红色变成黑色:

    图10

    但这样并不算完,因为凭空多出的黑色节点打破了规则5,所以发生连锁反应,需要继续把节点25从黑色变成红色:


    图11

    此时仍然没有结束,因为节点25和节点27又形成了两个连续的红色节点,需要继续把节点27从红色变成黑色:


    图12

    看完变色,再来看看旋转。

    左旋转
    逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。

    图13

    右旋转
    顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。

    图14

    继续刚才的例子,经过刚才调整后红黑树变成了下图所示:


    图15

    此时节点17和节点25是连续的两个红色节点,那么把节点17变成黑色节点?恐怕不合适。这样一来打破了规则4,而且根据规则2(根节点是黑色),也不可能把节点13变成红色节点。
    这时我们可以把节点13看做X,把节点17看做Y,像刚才的示意图那样进行左旋转:

    图16
    图17
    图18

    由于根节点必须是黑色节点,所以需要变色,变色结果如下:


    图19

    这样就结束了吗?并没有。因为其中两条路径(17 -> 8 -> 6 -> NIL)的黑色节点个数是4,其他路径的黑色节点个数是3,不符合规则5。
    这时候我们需要把节点13看做X,节点8看做Y,像刚才的示意图那样进行右旋转:


    图20
    图21
    图22

    最后根据规则来进行变色:


    图23

    如此一来,我们的红黑树变得重新符合规则。关于红黑树自平衡的调整,插入和删除节点的时候会涉及到很多种Case,都会采取不同的操作,但是核心思想都是为了维持五条规则。

    此外,红黑树还有一条定理,即红黑树从根到叶子的最长路径不会超过最短路径的两倍。这条定理是怎么来的呢?它是通过红黑树的五条规则推导而来。我们看上述规则5可知,从根节点到任意一个叶子节点,经过的黑色节点数目是相同的。而规则4又规定,不能有两个红色节点连在一起。于是我们这样考虑,假设从根节点到每个叶子节点,都是经过3个黑色节点,那么怎样才能让路径尽可能长?答案当然是尽量在黑色节点中插入红色节点,用红色节点充数。但是由于规则4,所以红色节点不能多放。于是,理论上的最长路径,一定是一个黑节点,连着一个红节点,再连着一个黑节点,以此类推。我们之前假设每条路径都是3个黑节点,再加上规则2说根节点一定是黑色,所以从根节点到子节点最多只能有6个节点,三黑三红交叉连接。那理论上的最短路径又是如何?当然是不包含任何一个红色节点,也就是只有3个黑节点。也就是说,理论上,最长路径,最多是最短路径的两倍。这个结论就限制了红黑树的最大平衡差,也就保证了不会出现图6的情况。

    红黑树的应用有很多,jdk的集合类TreeMap和TreeSet底层都是就是用红黑树实现的,在java8中,HashMap也用到了红黑树。

    与b树和b+树对比看,红黑树适合小数据范围内的快速查找(比如map当量的容器)(查询时间复杂度是O(logN))。b树适合大范围数据查找(db场合)。

    而与完全二叉树相比,红黑树的查找、插入和删除的时间复杂度都是O(log n),而完全二叉树的查找时间复杂度也是O(log n),但插入和删除的时间复杂度为O(n)。因此,在实际应用中,红黑树通常比完全二叉树更受欢迎,因为它在维护有序性的同时,具有较好的平衡性能。


    引用:https://blog.csdn.net/qq_36610462/article/details/83277524 原文讲的非常好,建议大家学习。
    https://www.cnblogs.com/tuyang1129/p/12556519.html

    相关文章

      网友评论

          本文标题:红黑树

          本文链接:https://www.haomeiwen.com/subject/ckzklltx.html