• 数据结构与算法(十三)——红黑树2 Craftsman


    三、删除

    1、介绍

      红黑树的删除类似于排序二叉树,排序二叉树主要分为三种情况:
      (1)删除没有左孩子且没有右孩子的结点。即:度为0。
      (2)删除只有左(右)孩子的结点。即:度为1。
      (3)删除有左孩子且有右孩子的结点。即:度为2。
      由于红黑树还有颜色的区分,所以在上述三种情况的基础上加上颜色,就是六种情况。以 {15, 7, 45, 3, 10, 25, 55, 1, 5, 75} 为例:

       红黑树有六种情况:
      (1)删除度为 0 的黑色结点。比如:10、25。
      (2)删除度为 0 的红色结点。比如:1、5、75。
      (3)删除度为 1 的黑色结点。比如:55。
      (4)删除度为 1 的红色结点。这种情况不存在。
      (5)删除度为 2 的黑色结点。比如:3、15。
      (6)删除度为 2 的红色结点。比如:7、45。

    2、说明

      论证:度为 1 的红色结点,在红黑树中,是不存在的!
      所有度为 1 的情况只有以下 4 种,这里都画的右孩子,左孩子也是同理。

      其中:
      "黑-黑"和"红-黑",这两种情况,都不符合红黑树的性质(4)从任意一个结点到其所有叶子结点,所经过的黑色结点数目必须相等。
      "红-红",不符合红黑树的性质(5)所有红色结点的两个孩子结点必须是黑色(即,红色结点不能连续)。
      只有"黑-红"这种情况存在。所以,度为 1 的结点,也必然是"黑-红"这种情况。

    3、分析

      情况(1)删除度为 0 的黑色结点:比较复杂,后面专门讨论。
      情况(2)删除度为 0 的红色结点:直接删除即可。
      情况(3)删除度为 1 的黑色结点:必然是"黑-红"的结构,则,删除当前结点(A),让孩子结点(B)代替A,并将B改为黑色。
      情况(4)删除度为 1 的红色结点:这种情况不存在。
      情况(5)删除度为 2 的黑色结点:
      比如:删除 15,用其前驱10(后继也可以)的值代替15,再删除10(跳到情况1)即可。

      比如:删除 15,用其前驱10(后继也可以)的值代替15,再删除10(跳到情况3)即可。

      比如:删除 15,用其前驱12(后继也可以)的值代替15,再删除12(跳到情况2)即可。

      情况(6)删除度为 2 的红色结点:同情况(5),不再赘述。
      下面,专门讨论情况(1)删除度为 0 的黑色结点。为了方便描述,先约定一下结点名称。

     

      由于树的左子树和右子树是对称的,所以只讨论一边的情况即可。不妨令待删除结点 C 为左孩子,右孩子的对称情况同理即可。

    4、兄弟结点B是红

      B是红:则 P 一定是黑色。BL、BR一定存在且是黑色。
      调整方案:先对 P 左旋;然后B 和 P 互换颜色。(需注意旋转后,这里的 B 就不是 C 的兄弟结点。后面的描述不赘述)。此时跳转到下面 B(此时的B是BL,BL才是C的兄弟结点) 是黑的情况。

    5、兄弟结点B是黑

      B是黑:则孩子结点BL和BR要么不存在,要么存在且为红色。不可能是黑色的结点,这会违背性质(4)从任意一个结点到其所有叶子结点,所经过的黑色结点数目必须相等。
      情况一:BR存在且为红,B的度为1。这里包含了度为2的情况。
      调整方案:先对 P 左旋;然后B 和 P 互换颜色,将BR涂黑;最后直接删除C。

      情况二:BR不存在,BL存在且为红,B的度为1。
      调整方案:先对 B 右旋;然后BL 和 B 互换颜色;跳转到上面的情况一;

      情况三:BL、BR都不存在,B的度为0。
      调整方案:这里,又要分两种情况讨论,P是红色还是黑色?
      (1)P是红色
      调整方案:P 和 B 互换颜色;直接删除C。

      (2)P是黑色
      调整方案:将 B 涂红;直接删除C;将node指向 P,递归进行平衡调整(不再删除结点),直到 node 指向根 root 结点。
      说明:最后一步有点不好理解。删除C后,P的左右子树黑色结点数相等了。但是经过P的路径,即:G(P的父结点)的左(右)子树黑色结点数会减 1。所以,需要递归调整 P。

    6、代码

      代码示例:完整的红黑树插入及删除

      1 public class RBTree<T extends Comparable<T>> {
      2     // 根结点
      3     private RBNode<T> root;
      4 
      5     public RBTree() {
      6     }
      7 
      8     public RBTree(T[] arr) {
      9         if (arr == null || arr.length == 0) {
     10             return;
     11         }
     12 
     13         for (T i : arr) {
     14             this.add(i);
     15         }
     16     }
     17 
     18     // 查找结点 t
     19     public RBNode<T> findRbNode(T t) {
     20         return this.findRbNode(t, root);
     21     }
     22 
     23     private RBNode<T> findRbNode(T t, RBNode<T> node) {
     24         if (t == null || node == null) {
     25             return null;
     26         }
     27 
     28         if (t.compareTo(node.value) == 0) {
     29             return node;
     30         }
     31         if (t.compareTo(node.value) < 0) {
     32             return this.findRbNode(t, node.left);
     33         } else {
     34             return this.findRbNode(t, node.right);
     35         }
     36     }
     37 
     38     // 查找结点 t 的前驱
     39     private RBNode<T> precursor(T t) {
     40         final RBNode<T> node = this.findRbNode(t);
     41         if (node == null) {
     42             return null;
     43         }
     44         return this.precursor(node);
     45     }
     46 
     47     private RBNode<T> precursor(RBNode<T> node) {
     48         // 左子树的最大值
     49         if (node.left != null) {
     50             RBNode<T> t = node.left;
     51             while (t.right != null) {
     52                 t = t.right;
     53             }
     54             return t;
     55         } else {
     56             // 这里在删除的情况下是不存在的.但是,就找前驱后继来说是存在的.
     57             RBNode<T> temp = node.parent;
     58             RBNode<T> ch = node;
     59             while (temp != null && ch == temp.left) {
     60                 ch = temp;
     61                 temp = temp.parent;
     62             }
     63 
     64             return temp;
     65         }
     66     }
     67 
     68     // 查找结点 t 的后继
     69     private RBNode<T> successor(T t) {
     70         final RBNode<T> node = this.findRbNode(t);
     71         if (node == null) {
     72             return null;
     73         }
     74         return this.successor(node);
     75     }
     76 
     77     private RBNode<T> successor(RBNode<T> node) {
     78         // 右子树的最小值
     79         if (node.right != null) {
     80             RBNode<T> t = node.right;
     81             while (t.left != null) {
     82                 t = t.left;
     83             }
     84             return t;
     85         } else {
     86             // 这里在删除的情况下是不存在的.但是,就找前驱后继来说是存在的.
     87             RBNode<T> temp = node.parent;
     88             RBNode<T> ch = node;
     89             while (temp != null && ch == temp.right) {
     90                 ch = temp;
     91                 temp = temp.parent;
     92             }
     93 
     94             return temp;
     95         }
     96     }
     97 
     98     public void delete(T value) {
     99         final RBNode<T> node = this.findRbNode(value);
    100         if (node == null) {
    101             System.out.println("待删除的结点:" + value + " 不存在~");
    102             return;
    103         }
    104 
    105         this.delNode(node);
    106     }
    107 
    108     private void delNode(RBNode<T> node) {
    109         final int degree = node.getDegree();
    110         // 度为 0
    111         if (degree == 0) {
    112             // 1.红色.直接删
    113             if (node.red) {
    114                 this.freeDegree0(node);
    115             } else {
    116                 // 2.黑色
    117                 if (node == root) {
    118                     this.freeDegree0(node);
    119                 } else {
    120                     this.delBlackNode(node);
    121                 }
    122             }
    123         } else if (degree == 1) {
    124             // 度为 1.一定是 "黑-红"
    125             if (node.left != null) {
    126                 node.value = node.left.value;
    127                 node.left = null;
    128             } else {
    129                 node.value = node.right.value;
    130                 node.right = null;
    131             }
    132         } else {
    133             // 度为 2
    134             final RBNode<T> precursor = this.precursor(node);
    135             node.value = precursor.value;
    136             this.delNode(precursor);
    137         }
    138     }
    139 
    140     /* 删除度为 1 的黑色结点 */
    141     private void delBlackNode(RBNode<T> node) {
    142         RBNode<T> temp = node;
    143 
    144         // 递归调整
    145         while (temp != root) {
    146             final RBNode<T> p = temp.parent;
    147             final RBNode<T> brother = temp.getBrother();
    148 
    149             // 兄弟 B是红
    150             if (brother.red) {
    151                 this.adjustCase1(temp); // 经过adjustCase1后,兄弟是黑色
    152             } else {
    153                 // 兄弟 B是黑 .有孩子
    154                 if (brother.left != null || brother.right != null) {
    155                     if (temp == p.left) {
    156                         if (brother.right != null) {
    157                             this.adjustCase2(temp);
    158                         } else {
    159                             this.adjustCase3(temp);
    160                         }
    161                     } else {
    162                         if (brother.left != null) {
    163                             this.adjustCase2(temp);
    164                         } else {
    165                             this.adjustCase3(temp);
    166                         }
    167                     }
    168 
    169                     break;
    170                 } else {
    171                     // C-黑.兄弟 B是黑. 且没有孩子
    172                     // p-红
    173                     if (p.red) {
    174                         brother.red = true;
    175                         p.red = false;
    176                         this.freeDegree0(temp);
    177                         break;
    178                     } else {
    179                         // p-黑
    180                         brother.red = true;
    181                         this.freeDegree0(temp);
    182                         temp = p;
    183                     }
    184                 }
    185             }
    186         }
    187     }
    188 
    189     // C-黑. B-红
    190     private void adjustCase1(RBNode<T> node) {
    191         final RBNode<T> p = node.parent;
    192         // 左孩子.(左右对称的)
    193         if (node == p.left) {
    194             this.leftRotate(p);
    195         } else {
    196             this.rightRotate(p);
    197         }
    198 
    199         node.parent.red = true;
    200         node.parent.parent.red = false;
    201     }
    202 
    203     // C-黑. B-黑. BR-红 (远侄子)
    204     private void adjustCase2(RBNode<T> node) {
    205         final RBNode<T> p = node.parent;
    206         if (node == p.left) {
    207             this.leftRotate(p);
    208 
    209             // B、P颜色互换
    210             node.parent.parent.red = node.parent.red;
    211             node.parent.red = false;
    212             // 涂黑远侄子
    213             node.parent.parent.right.red = false;
    214         } else {
    215             this.rightRotate(p);
    216 
    217             // B、P颜色互换
    218             node.parent.parent.red = node.parent.red;
    219             node.parent.red = false;
    220             // 涂黑远侄子
    221             node.parent.parent.left.red = false;
    222         }
    223         this.freeDegree0(node);
    224     }
    225 
    226     // C-黑. B-黑. BR-不存在. BL-红 (近侄子)
    227     private void adjustCase3(RBNode<T> node) {
    228         final RBNode<T> p = node.parent;
    229         final RBNode<T> brother = node.getBrother();
    230         // C 是左孩子.BL-红 (近侄子)
    231         if (brother.left != null) {
    232             rightRotate(brother);
    233         } else {
    234             // C 是右孩子.BR-红 (近侄子)
    235             leftRotate(brother);
    236         }
    237 
    238         // BL 和 B 互换颜色
    239         brother.red = true;
    240         brother.parent.red = false;
    241 
    242         // 跳转到adjustCase2
    243         this.adjustCase2(p);
    244     }
    245 
    246     // 直接删除度为 0 的结点 node
    247     private void freeDegree0(RBNode<T> node) {
    248         final RBNode<T> p = node.parent;
    249         // 待删除结点 node 就是root
    250         if (p == null) {
    251             root = null;
    252             return;
    253         }
    254 
    255         if (node == p.left) {
    256             p.left = null;
    257         } else {
    258             p.right = null;
    259         }
    260     }
    261 
    262     // 添加结点
    263     public void add(T value) {
    264         RBNode<T> newNode = new RBNode<>(value);
    265         if (root == null) {
    266             root = newNode;
    267             newNode.red = false;
    268             return;
    269         }
    270 
    271         // 1.添加
    272         this.add(root, newNode);
    273 
    274         // 2.调整
    275         this.fixUp(newNode);
    276     }
    277 
    278     private void fixUp(RBNode<T> newNode) {
    279         if (newNode == root) {
    280             newNode.red = false;
    281             return;
    282         }
    283 
    284         // newNode 的父结点为黑色
    285         if (!newNode.parent.red) {
    286             return;
    287         }
    288 
    289         /* 1.newNode 的叔叔结点存在且为红色。*/
    290         final RBNode<T> uncle = newNode.getUncle();
    291         if (uncle != null && uncle.red) {
    292             newNode.parent.red = false;
    293             uncle.red = false;
    294 
    295             newNode.parent.parent.red = true;
    296             this.fixUp(newNode.parent.parent);
    297         } else {
    298             /* 2.newNode 的叔叔结点不存在,或者为黑色。*/
    299             final RBNode<T> grandFather = newNode.getGrandFather();
    300             // 2.1左左
    301             if (newNode == grandFather.left.left) {
    302                 this.rightRotate(grandFather);
    303                 newNode.parent.red = false;
    304                 grandFather.red = true;
    305             }
    306             // 2.2左右
    307             else if (newNode == grandFather.left.right) {
    308                 this.leftRotate(newNode.parent);
    309                 this.rightRotate(grandFather);
    310                 newNode.red = false;
    311                 grandFather.red = true;
    312             }
    313             // 2.3右右
    314             else if (newNode == grandFather.right.right) {
    315                 this.leftRotate(grandFather);
    316                 newNode.parent.red = false;
    317                 grandFather.red = true;
    318             }
    319             // 2.4右左
    320             else if (newNode == grandFather.right.left) {
    321                 this.rightRotate(newNode.parent);
    322                 this.leftRotate(grandFather);
    323                 newNode.red = false;
    324                 grandFather.red = true;
    325             }
    326         }
    327     }
    328 
    329     // 按 排序二叉树 的规则插入结点
    330     private void add(RBNode<T> root, RBNode<T> newNode) {
    331         if (newNode.value.compareTo(root.value) <= 0) {
    332             if (root.left == null) {
    333                 root.left = newNode;
    334                 newNode.parent = root;
    335             } else {
    336                 this.add(root.left, newNode);
    337             }
    338         } else {
    339             if (root.right == null) {
    340                 root.right = newNode;
    341                 newNode.parent = root;
    342             } else {
    343                 this.add(root.right, newNode);
    344             }
    345         }
    346     }
    347 
    348     // 左旋
    349     private void leftRotate(RBNode<T> node) {
    350         if (node == null) {
    351             return;
    352         }
    353         final RBNode<T> p = node.parent;
    354 
    355         // 左旋. 应该认为 temp 不为null
    356         final RBNode<T> temp = node.right;
    357         node.right = temp.left;
    358         if (temp.left != null) {
    359             // 该结点可能不存在
    360             temp.left.parent = node;
    361         }
    362 
    363         temp.left = node;
    364         node.parent = temp;
    365 
    366         // 旋转完成.修正根结点与父结点
    367         // 1.node为根结点
    368         if (node == root) {
    369             root = temp;
    370             temp.parent = null;
    371             return;
    372         }
    373 
    374         // 2.node不为根结点
    375         // node 为父结点的右孩子
    376         if (node == p.right) {
    377             p.right = temp;
    378         } else {
    379             p.left = temp;
    380         }
    381         temp.parent = p;
    382     }
    383 
    384     // 右旋
    385     private void rightRotate(RBNode<T> node) {
    386         if (node == null) {
    387             return;
    388         }
    389 
    390         final RBNode<T> p = node.parent;
    391 
    392         // 右旋. 应该认为 temp 不为null
    393         final RBNode<T> temp = node.left;
    394         node.left = temp.right;
    395         if (temp.right != null) {
    396             // 该结点可能不存在
    397             temp.right.parent = node;
    398         }
    399 
    400         temp.right = node;
    401         node.parent = temp;
    402 
    403         // 旋转完成.修正根结点与父结点
    404         // 1.node为根结点
    405         if (node == root) {
    406             root = temp;
    407             temp.parent = null;
    408             return;
    409         }
    410 
    411         // 2.node不为根结点
    412         // node 为父结点的右孩子
    413         if (node == p.right) {
    414             p.right = temp;
    415         } else {
    416             p.left = temp;
    417         }
    418         temp.parent = p;
    419     }
    420 
    421     // 中序遍历
    422     public void infixOrder() {
    423         this.infixOrder(root);
    424     }
    425 
    426     private void infixOrder(RBNode<T> root) {
    427         if (root != null) {
    428             this.infixOrder(root.left);
    429             System.out.print("-->" + root.value + ":" + (root.red ? "红" : "黑"));
    430             this.infixOrder(root.right);
    431         }
    432     }
    433 
    434     /**
    435      * 红黑树 树结点结构
    436      */
    437     protected static class RBNode<T extends Comparable<T>> {
    438         private T value;
    439         // 默认为 红色 结点
    440         private boolean red = true;
    441 
    442         private RBNode<T> left;
    443         private RBNode<T> right;
    444         private RBNode<T> parent;
    445 
    446         public RBNode() {
    447         }
    448 
    449         public RBNode(T value) {
    450             this.value = value;
    451         }
    452 
    453         // 返回结点的度
    454         public int getDegree() {
    455             if (this.left == null && this.right == null) {
    456                 return 0;
    457             }
    458 
    459             if ((this.left != null && this.right == null) || (this.left == null && this.right != null)) {
    460                 return 1;
    461             }
    462 
    463             return 2;
    464         }
    465 
    466         public RBNode<T> getUncle() {
    467             final RBNode<T> grandFather = this.parent.parent;
    468             final RBNode<T> parent = this.parent;
    469 
    470             if (parent == grandFather.left) {
    471                 return grandFather.right;
    472             }
    473 
    474             if (parent == grandFather.right) {
    475                 return grandFather.left;
    476             }
    477 
    478             return null;
    479         }
    480 
    481         public RBNode<T> getGrandFather() {
    482             return this.parent.parent;
    483         }
    484 
    485         public RBNode<T> getBrother() {
    486             final RBNode<T> p = this.parent;
    487 
    488             return this == p.left ? p.right : p.left;
    489         }
    490 
    491         @Override
    492         public String toString() {
    493             return "RBNode{" +
    494                     "value=" + value +
    495                     ", red=" + red +
    496                     '}';
    497         }
    498     }
    499 }
    完整的红黑树插入及删除

      代码示例:测试

     1 public class Main {
     2     public static void main(String[] args) {
     3         // Integer[] integers = {15, 7, 45, 3, 10, 25, 55, 1, 5, 75};
     4         Integer[] integers = {500, 100, 750, 25, 300, 550, 800, 15, 50, 520, 600, 510};
     5         RBTree<Integer> tree = new RBTree<>(integers);
     6         tree.infixOrder();
     7 
     8         tree.delete(300);
     9         System.out.println("");
    10         tree.infixOrder();
    11     }
    12 }

      最后,推荐一个在线构建红黑树的地址:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html  用于读者验证上述代码的结果。上述测试案例构建的红黑树为:

  • 相关阅读:
    指针型函数与函数型指针 -2021.08.04
    Ubuntu18.04 NAT模式下配置静态IP地址 -2020.11.09
    Linux编译内核 Ubuntu18.04 -2020.11.04
    以PING为例,利用Wireshark深入理解网络层、数据链路层的工作原理 -2020.10.30
    Ubuntu18.04虚拟机的安装
    UNIX/Linux系统中的文件属性
    【计算机四级嵌入式】内存管理
    利用预编译解决C/C++重复定义的错误 -2020.09.13
    使用镜像安装cygwin、gcc并配置CLion IDE -2020.09.12
    Android Studio 4.0.1 找不到R.java 2020.09.08
  • 原文地址:https://www.cnblogs.com/originator/p/16065705.html
Copyright © 2020-2023  润新知