48张图让你彻底理解红黑树!!!(内附代码及实践网页)

一直知道红黑树及其特点,但是对具体的算法没有研究,最近心血来潮,打算整理一下具体的特点和算法。看了很多文章,大多数只是讲了操作方式,而没有讲述背后的原因、操作的原理,因此即使背过了,也是知其然而不知其所以然。本篇文章的目的,是希望能用最清晰的表达,描述红黑树的操作和原理。为了方便验证想法,特制作红黑树演示网页,可以对照着文章进行实践操作,希望大家可以在其中感受红黑树的优美:红黑树演示

  1. 本文章版权归原作者 木木田(YT315) 所有;
  2. 未经原作者允许不得转载本文内容,否则将视为侵权;
  3. 转载或者引用本文内容请注明来源及原作者;
  4. 对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。

简介:

为什么要有二叉树:

为了实现快速查找。查找是在软件开发中经常需要的,因此人们为了查找速度更快,提出了各种数据结构,如哈希表,B树,排序二叉树等,本文讲的红黑树属于排序二叉树也属于B树。大家常说的二叉树一般指排序二叉树,后面我们都简称为二叉树。

下面举例描述二叉树如何提高查询速度:

首先我们有一组数据 [18,13,30,10,15,20,50],组成一个数组。

从其中查找一个数,一般操作是遍历数组,依次判断是不是目标数。假设想要查找50,则从18开始,依次判断,需要判断7次才能找到。在真实应用中,当数据量庞大时,需要判断非常多次,这样会非常耗时,时间复杂度是O(N)。

二叉树是由无数节点构成,每个节点都会指向另外两个子节点,子节点比自己小,子节点比自己大,最上面的节点是根节点。如果我们把上述数据存储在二叉树中,将如下图所示。同样,如果要查50号数据,从根节点18开始:

  1. 判断50大于18,查找18的子节点得到30

  2. 判断50大于30,查找30的子节点得到50

  3. 判断50等于50,说明找到目标节点了

一共判断了3次,就找到了目标数据,时间复杂度是O(logN),底数为2。

请添加图片描述

上面演示为了方便只用了7个数据,二叉树比数组查询只是少了4次判断。但如果有100万个数据,用平衡二叉树最多只需要判断20次,而数组最多要判断100万次。以该例子可看出,当数据量庞大时,可大大缩短查询时间。

什么是平衡二叉树:

如果有同学仔细看上面那句话,会发现我用的是“平衡二叉树”而不是“二叉树”,两者有什么区别呢?

只要满足每个节点有两个子节点,子节点比自己小,子节点的比自己大,且子节点也可以为空的条件的树都可以称为二叉树(排序二叉树)。如图所示的数据结构也可以称为二叉树,但是它的查询效率和数组一样,时间复杂度是O(N),没有达到快速查询的目标。
在这里插入图片描述
平衡二叉树的特点是,左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在构建二叉树时,每当添加新的节点时,需要对二叉树是否平衡进行检查,然后考虑如何旋转来达到平衡(旋转会在后面章节讲解),从而构建出如下图所示的平衡二叉树。
请添加图片描述
综上所述,平衡二叉树属于二叉树,平衡二叉树能够快速查询目标。

红黑树又是什么:

红黑树如下图所示,是一种特殊平衡二叉树,但是没有平衡二叉树那么严格(左右子树高度差的绝对值可以超过1),主要有以下五个性质(建议先复制或截图,放到一边参考,后面内容会多次引用到):

  1. 每个节点都是红色或者黑色

  2. 根节点是黑色

  3. 所有叶子节点都是黑色并且是空的

  4. 每个红色节点的子节点都是黑色

  5. 从红黑树的任一节点,到其任意叶子节点的所有路径,均包含相同数目的黑色节点

    如下图,从根节点到叶子节点一共有以下几条路径,其中加粗斜体为黑色节点,可以看出每条路径黑色节点数量相同,均为2个:

    13-11-10-9

    13-11-12

    13-15-14

    13-15-17-16

    13-15-17-18
    在这里插入图片描述
    刚看到这五个性质一定会有点懵,不过前3个性质都是字面意思,后2个性质我们来分析一下:

  • 由性质4可以得出推论:不存在两个连续的红色节点,因为红色节点不允许成为红色节点的子节点。

  • 由性质5结合性质4可以得出推论:红黑树根节点的两个子树极限差距如下图所示,子树全都是黑色节点,右子树为红黑相间,子树的高度是子树的2倍,所以红黑树为结构不严格的平衡二叉树。
    在这里插入图片描述

黑高是什么:

任何一个节点(不包含该节点)到达其任意叶子节点的路径上,黑色结点的个数即称为该结点的黑高,对应上述性质5可知,红黑树每个节点的黑高是一个确定的数。黑高是红黑树中一个很重要的概念。

如下图所示,22号节点到叶子节点的路径如下:

  • 22-24-26-27

  • 22-24-26-25

  • 22-24-23

  • 22-21

其中加粗的为经过的黑色节点(不包含该结点),可见经过的黑色节点数量都是1,即22号节点的黑高为1。

在这里插入图片描述

二叉树旋转操作

前面讲过,平衡二叉树会通过旋转来达到平衡。我们所说的二叉树指排序二叉树,即子节点比当前节点小,子节点比当前节点大,旋转操作不能破坏此原则。

左旋:

如下图所示为父节点1号节点旋过程,步骤如下:

  1. 父节点的子节点代替自己的位置 (3号节点代替1号节点位置)

  2. 子节点的子节点成为父节点的子节点(3号节点原来的子节点2号,成为1号节点的子节点)

  3. 父节点成为子节点的子节点(1号节点成为3号节点的子节点)

可见旋转前后依然遵循排序。

在这里插入图片描述代码:

function leftRotate(node) {
   let newNode = node.right
   if (newNode == null) return
   //移交父
   newNode.parent = node.parent
   if (node.parent != null) {
      if (node.parent.left == node) node.parent.left = newNode
      else if (node.parent.right == node) node.parent.right = newNode
   } else {
      rootNode = newNode
   }
   //移交左弟
   node.right = newNode.left
   if (newNode.left != null) {
      newNode.left.parent = node
   }
   //相互关系
   newNode.left = node
   node.parent = newNode
}

右旋:

右旋和左旋对称,如下图所示父节点3号节点旋过程:

  1. 父节点的子节点代替自己的位置(1号节点代替3号节点位置)

  2. 子节点的子节点成为父节点的节点(1号节点原来的子节点2号,成为3号节点的子节点)

  3. 父节点成为子节点的子节点(3号节点成为1号节点的子节点)

在这里插入图片描述代码:

function rightRotate(node) {
   let newNode = node.left
   if (newNode == null) return
   //移交父
   newNode.parent = node.parent
   if (node.parent != null) {
      if (node.parent.left == node) node.parent.left = newNode
      else if (node.parent.right == node) node.parent.right = newNode
   } else {
      rootNode = newNode
   }
   //移交右弟
   node.left = newNode.right
   if (newNode.right != null) {
      newNode.right.parent = node
   }
   //相互关系
   newNode.right = node
   node.parent = newNode
}

红黑树调整的算法:

对于红黑树的操作主要有插入节点和删除节点,但是当插入或者删除的时候可能会打破红黑树的5个性质,因此需要调整算法来维护红黑树。在调整前,我们认为树当前是平衡的,符合红黑树的5个性质。

插入节点:

情况分析

二叉树的节点只能插入到叶子节点位置,我们将要插入的节点默认初始为红色,插入节点可能会出现以下情况:

  • 树还是空的,此时插入的节点直接作为根节点,并把颜色更改为黑色。

  • 父节点为黑色,此时直接插入,不影响树的5个性质,不影响黑高,不需要调整。如下图所示当需要插入7时:
    在这里插入图片描述

  • 父节点为红色,当前节点也是红色,此时就违反了第4性质,此时需要变色和旋转配合调整来使树再次满足性质,此时会出现以下几种情况:

    1. 情况: 当前节点是父节点的子节点,父节点是爷节点的子节点,爷节点的子节点为黑色或者空(此时爷节点一定是黑色,因为父节点是红,根据性质4)。

      操作: 将爷节点旋,然后将父节点设置为黑色,爷节点设置为红色

      原理: 此时为了满足性质4,通过旋和变色,让子树留一个红色节点,并且给子树一个红色节点,这样两边的黑高都没有受到影响。

      在这里插入图片描述如果叔节点为空,同理
      在这里插入图片描述

    2. 情况: 当前节点是父节点的子节点,父节点是爷节点的子节点,爷节点的子节点为黑色或者空(此时爷节点一定是黑色,因为父节点是红,根据性质4)。

      操作: 将父节点旋,则可以变成情况1的样子,然后使用情况1方式调整。

      原理: 同情况1

      在这里插入图片描述在这里插入图片描述

    3. 情况: 当前节点是父节点的左/右子节点,父节点是爷节点的子节点,爷节点的子节点为红(此时爷节点一定是黑色,因为父节点是红,根据性质4)。

      操作: 将父节点和叔节点都变为黑色,将爷节点变为红色,然后将爷节点设为目标节点递归调用调整算法,直到满足红黑性质,或者当前节点递归为根节点时,将颜色变为黑色,则停止调整。

      原理: 此时3个红色一个黑色,无论怎么旋转都不能满足性质4,因此将父节点和叔节点都变为黑色,将爷节点变为红色,则可满足红黑树性质,但是爷节点的父节点有可能也是红色,因此需将当前节点设置为爷节点,再使用此算法递归进行调整。此套算法的目的就是假设目标节点为新添加的节点且是红色,然后判断当前是否满足红黑树性质,如果不满足则通过调整树使树满足红黑树性质。

      在这里插入图片描述
      在这里插入图片描述

      以下3种情况和以上3种对称,这里不在多赘述原因

    4. 情况: 当前节点是父节点的子节点,父节点是爷节点的子节点,爷节点的子节点为黑色或者空(此时爷节点一定是黑色,因为父节点是红,根据性质4).

      操作: 将爷节点旋,然后将父节点设置为黑色,爷节点设置为红色

      原理: 同情况1

      在这里插入图片描述

      如果叔节点为空,同理
      在这里插入图片描述

  1. 情况: 当前节点是父节点的子节点,父节点是爷节点的子节点,爷节点的子节点为黑色或者空(此时爷节点一定是黑色,因为父节点是红,根据性质4)。

    操作: 将父节点旋,则可以变成情况4的样子,然后使用情况4方式调整

    原理: 同情况2
    在这里插入图片描述 在这里插入图片描述

  2. 情况: 当前节点是父节点的右/左子节点,父节点是爷节点的子节点,爷节点的子节点为红(此时爷节点一定是黑色,因为父节点是红,根据性质4).

    操作: 将父节点和叔节点都变为黑色,将爷节点变为红色,然后将爷节点设为目标节点递归调用调整算法,直到满足红黑性质,或者当前节点为根节点时,将颜色变为黑色,则停止调整.

    原理: 同情况3
    在这里插入图片描述在这里插入图片描述

总结

对于红黑树的插入算法难点主要在插入时父节点是红色的情况,因为如果父节点是黑色,插入红色,依然符合红黑树的性质,而父节点是红色则不满足性质4,需要进行调整至符合性质。

对于父节点是红色,有6种情况,后3种和前3种对称,前3种为父节点是爷节点的子节点的情况,后3种相反。用前三种举例,由于第2种情况对父节点进行左旋后就变成第1种情况了,因此对于父节点为红色主要可以总结为2种情况:

  1. 叔节点为黑色。此时可以通过变色和旋转,将父节点侧子树的一个红色移动到叔节点侧,然后达到平衡,同时根节点(爷节点原位置)的颜色依然可以保持为黑色不变,因此不会影响上面的树,对应情况1,2,4,5。

  2. 叔节点为红色。此时通过把父节点和叔节点变为黑色,并将爷节点变为红色,即可达到平衡,但是爷节点的父节点可能也是红色,因此需要将目标节点设置为爷节点,继续调用此算法递归,直到平衡,对应情况3,6。

举例

当前树如下图所示,现需要添加一个新的节点,值为11。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZA1FZvWw-1688871662296)(.\P35.jpg)]

一、寻找新节点放置位置的步骤如下:

  1. 从根节点开始11>3,再比较右节点 5

  2. 11>5,比较右节点 12

  3. 11<12,比较左节点 9

  4. 11>9,比较左节点 10

  5. 11>10,比较右节点为空

  6. 将新节点添加到10号的右子节点位置,如下图所示:

在这里插入图片描述

二、调整平衡至符合性质的步骤如下:

  1. 上图中,以11为当前节点,符合情况6,对应操作后得到下图,9号变为当前节点。

在这里插入图片描述

  1. 上图中,9为当前节点,符合情况5,对应操作后得到下图,12号变为当前节点。

在这里插入图片描述

  1. 上图中,12为当前节点,符合情况4,对应操作后得到下图,已经调整完成。

在这里插入图片描述

代码
 function afterAdd(node) {
         let startNode
         do {
            startNode = node
            if (node.parent == null) {//添加到根节点,或者向上递归到根节点
               rootNode = node
               node?.setColor("black")
            } else {//非根节点
               if (node.parent.isRed) {//父节点红色
                  let grand = node.parent.parent;//爷节点
                  let parent = node.parent;      //父节点
                  let parentIsLeft = (grand.left == parent)//父节点是否为爷节点的**左**节点
                  let uncle = parentIsLeft ? grand.right : grand.left//叔节点

                  if (uncle == null || !uncle.isRed) {
                     if (parentIsLeft) {
                        if (node == parent.left) {
                           parent?.setColor("black")
                           grand?.setColor("red")
                           rightRotate(grand)
                        } else {
                           leftRotate(parent)
                           node = parent
                        }
                     } else {
                        if (node == parent.right) {
                           parent?.setColor("black")
                           grand?.setColor("red")
                           leftRotate(grand)
                        } else {
                           rightRotate(parent)
                           node = parent
                        }
                     }
                  } else {//叔父节点红色
                     grand?.setColor("red")
                     parent?.setColor("black")
                     uncle?.setColor("black")
                     node = grand;
                  }
               } else {//parent 是黑色
                  //直接添加,不用调整修复。就满足了红黑树的性质
               }
            }
         } while (startNode != node);//node被赋了新的值,则说明需要继续递归
      }

删除节点:

删除节点主要步骤为:

  1. 删除节点的位置结构需维持树形状

  2. 删除节点后调整后需符合红黑树性质

删除第一步:维持树形状

根据删除节点所在树中的位置结构,会有不同的删除方式,如:

  1. 删除的节点没有子节点,树形状不变。

  2. 删除的节点有1个子节点,该情况需要用子节点替换自己。

  3. 删除的节点有2个子节点,直接删除后子节点会脱离树结构,所以不可直接删除,需通过节点替换方式调整。

针对删除节点有2个子节点的情况分析:

从上述三点可看出,第三种情况无法直接删除,需要通过节点替换方式调整,找符合前两种情况的节点顶替自己,则删除情况又可以回归前两种。以下图为例:

  1. 目标:删除11节点。

  2. 11节点不可直接删除的原因:从图示可知,11节点有父节点且有2个子节点,若直接删除,则子节点直接脱离树结构,树结构破坏了。

  3. 关键:如果能将老父亲和两个小孩托付给别人,自己就可以放心的离开。故删除的前提为寻找合适的节点替代自己(找人托付,照顾老小)。

  4. 找节点:找到合适的替换节点(12节点)。
    在这里插入图片描述

  5. 调整树:删除后根据算法调整至符合树的性质。

在这里插入图片描述

如何寻找替换节点

合适的替换节点需要满足的要求为:

  1. 没有子节点,或者只有一个子节点。

  2. 替换后依然满足排序二叉树的性质。

    由二叉树的性质(子树小于自己,子树大于自己,同时自己也根据位置大于或小于父节点)可推算出,适合的节点就是被删除节点的前驱节点或后继节点。

前驱节点与后继节点是二叉树进行中序遍历时,当前节点的前一个与后一个。寻找这两个节点的方式很多,有需要也可再去查阅资料。下面简单描述一下寻找的算法

  • 寻找前驱节点举例

    如下图,若需要寻找23节点的前驱节点,步骤如下:

  1. 获取此节点的子节点(19)

  2. 然后从此节点开始递归寻找子节点,(19-21-22)

  3. 直到找到一个没有子节点的节点(22),即22节点为23节点的前驱节点

可以理解为,所有比当前节点小的子节点(子树)中最大的一个,这个节点一定可以来代替自己的位置,因为它比自己的所有子树都大,同样也比自己的所有子树都小。

在这里插入图片描述

 let Node= Node.left
 while (Node.right!= null) {
    Node= Node.right
 }
  • 寻找后继节点举例

    如下图,若需要寻找23节点的前驱节点,寻找的方式如下:

  1. 获取此节点的子节点(27)

  2. 然后从此节点开始递归寻找子节点,(27-25-24)

  3. 直到找到一个没有子节点的节点(24),即24节点为23节点的后继节点

可以理解为,所有比当前节点大的子节点(子树)中最小的一个,这个节点一定可以来代替自己的位置,因为它比自己的所有子树都大,同样也比自己的所有子树都小。

在这里插入图片描述

 let Node= Node.right
 while (Node.left != null) {
    Node= Node.left
 }
删除第二步:调整至符合红黑树性质

经过上述章节,删除节点的第一步骤以及完成,即树的形状已经确定。接下来需要进行第二步骤,即调整颜色至符合红黑树性质。

颜色主要有以下情况:

  1. 目标节点是红色,删除后不影响红黑树性质,故可直接删除。

  2. 目标节点是黑色,删除后会改变黑高,打破红黑树性质,所以需要进一步调整。

针对黑色的删除节点情况分析:
  1. 情况: 目标节点是父节点的子节点,兄弟节点是黑色,兄节点的子节点都是黑色或空,父节点颜色不限制。

    操作: 将兄节点变为红色,然后把目标节点设置为父节点,然后继续递归调整算法。

    原因: 当前目标节点为黑色,删除后父节点的子树黑高相当于减一。将兄节点变为红色,则父节点的子树黑高相当于减一。通过该种方法,则父节点恢复平衡,但父节点的黑高整体减一,爷节点失衡,此时需要将父节点当作目标节点,循环递归此算法。

    此套算法的目的就是假设目标节点处要删除一个节点,然后通过调整树使树继续遵循红黑树性质。

    在这里插入图片描述在这里插入图片描述

  2. 情况: 目标节点是父节点的子节点,兄弟节点是黑色,兄弟节点的子节点是红色,子节和父节点颜色不限制。

    操作: 把父节点旋后设为黑色,兄弟节点设为父节点的颜色(图中用蓝色),兄弟节点的子节点设为黑色。

    原因:当前目标节点为黑色,删除后,父节点的子树黑高减一。此时通过父节点左旋操作后,将父节点变为黑色,左侧黑高恢复。兄弟节点替换父节点位置,变为父节点颜色,右侧黑高减一,此时将侄由红色变黑色后,右侧黑高恢复。对于新的父节点而言,左右黑高恢复平衡。对于侄,和根节点的距离依然是经过一个黑色节点,所以不影响。

    在这里插入图片描述

  3. 情况: 目标节点是父节点的子节点,兄弟节点是黑色,兄弟节点的子节点是红色子节点为黑色或空,父节点颜色不限制。

    操作: 将兄节点变为红色,侄变为黑色,然后旋兄节点,然后继续递归调整算法。

    原因: 因为这样操作后,变成了情况2,所以继续递归使用情况2的操作完成调整。深究其原因,此时删除当前节点则父节点子树黑色高度减一,父节点的子树有一个红色节点(侄),但是直接旋的话,此节点和父节点都会变为子树,而子树的高度就不够了,因为兄节点已经变成到父节点位置.因此不能直接旋,而是通过旋兄节点将红色节点转到边,形成情况2。

    在这里插入图片描述

  4. 情况: 目标节点是父节点的子节点,兄弟节点是红色(由性质4推算出,父节点绝对黑色),(由性质5推算出,兄弟节点一定有两个黑色子节点)

    操作: 将兄弟节点变为黑色,父节点变为红色,将父节点旋,然后继续递归调整算法。

    原因: 因为这样操作后,目标节点不变,根据侄的子节点的情况,一定是上面情况1,2,3中的一种,然后使用对应的操作继续完成调整。

    考虑另外一种方案,为什么不直接把兄节点旋,直接变成情况2,因为这样,侄和父节点的黑色节点距离就多了一个黑色(侄),不符合性质5。

    在这里插入图片描述

    剩下还有4种情况,和上面个4种对称,目标节点是父节点的子节点

  5. 情况: 目标节点是父节点的子节点,兄弟节点是黑色,兄节点的子节点都是黑色或空,父节点颜色不限制

    操作: 将兄节点变为红色,然后把目标节点设置为父节点.

    原因: 和情况1对称

    在这里插入图片描述在这里插入图片描述

  6. 情况: 目标节点是父节点的子节点,兄弟节点是黑色,兄弟节点的子节点是红色,子节和父节点颜色不限制

    操作: 把父节点设为黑色,兄弟节点设为父节点的颜色(图中用蓝色),兄弟节点的子节点设为黑色,旋父节点

    原因: 和情况2对称

    在这里插入图片描述

  7. 情况: 目标节点是父节点的子节点,兄弟节点是黑色,兄弟节点的子节点是红色子节点为黑色或空,父节点颜色不限制

    操作: 将兄节点变为红色,侄变为黑色,然后旋兄节点,然后继续递归调整算法

    原因: 和情况3对称

    在这里插入图片描述

  8. 情况: 目标节点是父节点的子节点,兄弟节点是红色(父节点绝对黑色,性质4),(兄弟节点一定两个黑色子节点,性质5)

    操作: 将兄弟节点变为黑色,让后将父节点旋,然后继续递归调整算法

    原因: 和情况4对称

    在这里插入图片描述

总结:

对于红黑树的删除算法难点主要在,被删除节点是黑色的情况,因为如果是红色,删除后不影响黑高,依然符合红黑树的性质,而被删除节点为黑色,主要有8种情况,后4种和前4种对称,前4种为当前节点是父节点的子节点的情况,后4种相反。对于被删除节点为黑色主要可以总结为3种情况:

  1. 兄弟节点及兄弟节点的子节点都是黑色,此种情况直接将兄弟节点变为红色,然后将目标节点变为父节点继续递归调用调整算法。

  2. 兄弟节点为黑色,但是至少有一个子节点是红色,则可以通过旋转及变色调整使之平衡。

  3. 兄弟节点为红色,将父节点向目标节点方向旋转,然后将兄节点变为黑色,即父节点变为红色,则情况变成上面两种情况中的一种,进而继续调整。

举例:

当前树如下图所示,需要删除7号节点

在这里插入图片描述

7号节点的左右子节点都不为空,因此寻找其后继节点8号,如下图

在这里插入图片描述

将7号节点替换成其后继节点8号,问题转化为删除8号节点原来位置的问题,当前节点为8号节点原来位置,如下图

在这里插入图片描述

上图中,下面的8号为当前节点,符合情况1,对应操作后得到下图,9号变为当前节点

在这里插入图片描述

上图中,下面的9号为当前节点,符合情况4,对应操作后得到下图,9号依然为当前节点

在这里插入图片描述

上图中,下面的9号为当前节点,符合情况3,对应操作后得到下图,9号依然为当前节点

在这里插入图片描述

上图中,下面的9号为当前节点,符合情况2,对应操作后得到下图,以及调整完成,只需要把原来8号位置的待删除节点删掉

在这里插入图片描述

删除待删除的8号节点,即完成删除,如下图

在这里插入图片描述

代码:
function afterDelete(node) {
   while (node != rootNode && !node.isRed) {
      let parent = node.parent;
      let isLeft = (parent.left == node)
      let brother = isLeft ? parent.right : parent.left
      if (isLeft) {
         if (brother.isRed) {
            parent.setColor("red");
            brother.setColor("black");
            leftRotate(parent)
            continue;
         } else {
            if ((brother.left == null || !brother.left.isRed) && (brother.right == null || !brother.right.isRed))//双黑
            {
               brother.setColor("red")
               node = parent
               continue;
            } else if (brother.left?.isRed && (brother.right == null || !brother.right.isRed))//红黑
            {
               brother.left.setColor("black")
               brother.setColor("red")
               rightRotate(brother)
               continue;
            } else if (brother.right?.isRed) {
               brother.setColor(parent.color)
               parent.setColor("black")
               brother.right.setColor("black")
               leftRotate(parent)
               node = rootNode
            }
         }
      } else {
         if (brother.isRed) {
            parent.setColor("red");
            brother.setColor("black");
            rightRotate(parent)
            continue;
         } else {
            if ((brother.left == null || !brother.left.isRed) && (brother.right == null || !brother.right.isRed))//双黑
            {
               brother.setColor("red")
               node = parent
               continue;
            } else if (brother.right?.isRed && (brother.left == null || !brother.left.isRed))//红黑
            {
               brother.right.setColor("black")
               brother.setColor("red")
               leftRotate(brother)
               continue;
            } else if (brother.left?.isRed) {
               brother.setColor(parent.color)
               parent.setColor("black")
               brother.left.setColor("black")
               rightRotate(parent)
               node = rootNode
            }
         }
      }
   }
   node.setColor("black")
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值