数据结构与算法红黑树详解

2022年5月31日10:19:55

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、代码

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

数据结构与算法红黑树详解

  1publicclass RBTree<Textends Comparable<T>> {  2// 根结点  3private RBNode<T> root;  4  5public RBTree() {  6    }  7  8public RBTree(T[] arr) {  9if (arr ==null || arr.length == 0) { 10return; 11        } 12 13for (T i : arr) { 14this.add(i); 15        } 16    } 17 18// 查找结点 t 19public RBNode<T> findRbNode(T t) { 20returnthis.findRbNode(t, root); 21    } 22 23private RBNode<T> findRbNode(T t, RBNode<T> node) { 24if (t ==null || node ==null) { 25returnnull; 26        } 27 28if (t.compareTo(node.value) == 0) { 29return node; 30        } 31if (t.compareTo(node.value) < 0) { 32returnthis.findRbNode(t, node.left); 33         }else { 34returnthis.findRbNode(t, node.right); 35        } 36    } 37 38// 查找结点 t 的前驱 39private RBNode<T> precursor(T t) { 40final RBNode<T> node =this.findRbNode(t); 41if (node ==null) { 42returnnull; 43        } 44returnthis.precursor(node); 45    } 46 47private RBNode<T> precursor(RBNode<T> node) { 48// 左子树的最大值 49if (node.left !=null) { 50             RBNode<T> t = node.left; 51while (t.right !=null) { 52                 t = t.right; 53            } 54return t; 55         }else { 56// 这里在删除的情况下是不存在的.但是,就找前驱后继来说是存在的. 57             RBNode<T> temp = node.parent; 58             RBNode<T> ch = node; 59while (temp !=null && ch == temp.left) { 60                 ch = temp; 61                 temp = temp.parent; 62            } 63 64return temp; 65        } 66    } 67 68// 查找结点 t 的后继 69private RBNode<T> successor(T t) { 70final RBNode<T> node =this.findRbNode(t); 71if (node ==null) { 72returnnull; 73        } 74returnthis.successor(node); 75    } 76 77private RBNode<T> successor(RBNode<T> node) { 78// 右子树的最小值 79if (node.right !=null) { 80             RBNode<T> t = node.right; 81while (t.left !=null) { 82                 t = t.left; 83            } 84return t; 85         }else { 86// 这里在删除的情况下是不存在的.但是,就找前驱后继来说是存在的. 87             RBNode<T> temp = node.parent; 88             RBNode<T> ch = node; 89while (temp !=null && ch == temp.right) { 90                 ch = temp; 91                 temp = temp.parent; 92            } 93 94return temp; 95        } 96    } 97 98publicvoid delete(T value) { 99final RBNode<T> node =this.findRbNode(value);100if (node ==null) {101             System.out.println("待删除的结点:" + value + " 不存在~");102return;103        }104105this.delNode(node);106    }107108privatevoid delNode(RBNode<T> node) {109finalint degree = node.getDegree();110// 度为 0111if (degree == 0) {112// 1.红色.直接删113if (node.red) {114this.freeDegree0(node);115             }else {116// 2.黑色117if (node == root) {118this.freeDegree0(node);119                 }else {120this.delBlackNode(node);121                }122            }123         }elseif (degree == 1) {124// 度为 1.一定是 "黑-红"125if (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// 度为 2134final RBNode<T> precursor =this.precursor(node);135             node.value = precursor.value;136this.delNode(precursor);137        }138    }139140/* 删除度为 1 的黑色结点*/141privatevoid delBlackNode(RBNode<T> node) {142         RBNode<T> temp = node;143144// 递归调整145while (temp != root) {146final RBNode<T> p = temp.parent;147final RBNode<T> brother = temp.getBrother();148149// 兄弟 B是红150if (brother.red) {151this.adjustCase1(temp);// 经过adjustCase1后,兄弟是黑色152             }else {153// 兄弟 B是黑 .有孩子154if (brother.left !=null || brother.right !=null) {155if (temp == p.left) {156if (brother.right !=null) {157this.adjustCase2(temp);158                         }else {159this.adjustCase3(temp);160                        }161                     }else {162if (brother.left !=null) {163this.adjustCase2(temp);164                         }else {165this.adjustCase3(temp);166                        }167                    }168169break;170                 }else {171// C-黑.兄弟 B是黑. 且没有孩子172// p-红173if (p.red) {174                         brother.red =true;175                         p.red =false;176this.freeDegree0(temp);177break;178                     }else {179// p-黑180                         brother.red =true;181this.freeDegree0(temp);182                         temp = p;183                    }184                }185            }186        }187    }188189// C-黑. B-红190privatevoid adjustCase1(RBNode<T> node) {191final RBNode<T> p = node.parent;192// 左孩子.(左右对称的)193if (node == p.left) {194this.leftRotate(p);195         }else {196this.rightRotate(p);197        }198199         node.parent.red =true;200         node.parent.parent.red =false;201    }202203// C-黑. B-黑. BR-红 (远侄子)204privatevoid adjustCase2(RBNode<T> node) {205final RBNode<T> p = node.parent;206if (node == p.left) {207this.leftRotate(p);208209// 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 {215this.rightRotate(p);216217// B、P颜色互换218             node.parent.parent.red = node.parent.red;219             node.parent.red =false;220// 涂黑远侄子221             node.parent.parent.left.red =false;222        }223this.freeDegree0(node);224    }225226// C-黑. B-黑. BR-不存在. BL-红 (近侄子)227privatevoid adjustCase3(RBNode<T> node) {228final RBNode<T> p = node.parent;229final RBNode<T> brother = node.getBrother();230// C 是左孩子.BL-红 (近侄子)231if (brother.left !=null) {232            rightRotate(brother);233         }else {234// C 是右孩子.BR-红 (近侄子)235            leftRotate(brother);236        }237238// BL 和 B 互换颜色239         brother.red =true;240         brother.parent.red =false;241242// 跳转到adjustCase2243this.adjustCase2(p);244    }245246// 直接删除度为 0 的结点 node247privatevoid freeDegree0(RBNode<T> node) {248final RBNode<T> p = node.parent;249// 待删除结点 node 就是root250if (p ==null) {251             root =null;252return;253        }254255if (node == p.left) {256             p.left =null;257         }else {258             p.right =null;259        }260    }261262// 添加结点263publicvoid add(T value) {264         RBNode<T> newNode =new RBNode<>(value);265if (root ==null) {266             root = newNode;267             newNode.red =false;268return;269        }270271// 1.添加272this.add(root, newNode);273274// 2.调整275this.fixUp(newNode);276    }277278privatevoid fixUp(RBNode<T> newNode) {279if (newNode == root) {280             newNode.red =false;281return;282        }283284// newNode 的父结点为黑色285if (!newNode.parent.red) {286return;287        }288289/* 1.newNode 的叔叔结点存在且为红色。*/290final RBNode<T> uncle = newNode.getUncle();291if (uncle !=null && uncle.red) {292             newNode.parent.red =false;293             uncle.red =false;294295             newNode.parent.parent.red =true;296this.fixUp(newNode.parent.parent);297         }else {298/* 2.newNode 的叔叔结点不存在,或者为黑色。*/299final RBNode<T> grandFather = newNode.getGrandFather();300// 2.1左左301if (newNode == grandFather.left.left) {302this.rightRotate(grandFather);303                 newNode.parent.red =false;304                 grandFather.red =true;305            }306// 2.2左右307elseif (newNode == grandFather.left.right) {308this.leftRotate(newNode.parent);309this.rightRotate(grandFather);310                 newNode.red =false;311                 grandFather.red =true;312            }313// 2.3右右314elseif (newNode == grandFather.right.right) {315this.leftRotate(grandFather);316                 newNode.parent.red =false;317                 grandFather.red =true;318            }319// 2.4右左320elseif (newNode == grandFather.right.left) {321this.rightRotate(newNode.parent);322this.leftRotate(grandFather);323                 newNode.red =false;324                 grandFather.red =true;325            }326        }327    }328329// 按 排序二叉树 的规则插入结点330privatevoid add(RBNode<T> root, RBNode<T> newNode) {331if (newNode.value.compareTo(root.value) <= 0) {332if (root.left ==null) {333                 root.left = newNode;334                 newNode.parent = root;335             }else {336this.add(root.left, newNode);337            }338         }else {339if (root.right ==null) {340                 root.right = newNode;341                 newNode.parent = root;342             }else {343this.add(root.right, newNode);344            }345        }346    }347348// 左旋349privatevoid leftRotate(RBNode<T> node) {350if (node ==null) {351return;352        }353final RBNode<T> p = node.parent;354355// 左旋. 应该认为 temp 不为null356final RBNode<T> temp = node.right;357         node.right = temp.left;358if (temp.left !=null) {359// 该结点可能不存在360             temp.left.parent = node;361        }362363         temp.left = node;364         node.parent = temp;365366// 旋转完成.修正根结点与父结点367// 1.node为根结点368if (node == root) {369             root = temp;370             temp.parent =null;371return;372        }373374// 2.node不为根结点375// node 为父结点的右孩子376if (node == p.right) {377             p.right = temp;378         }else {379             p.left = temp;380        }381         temp.parent = p;382    }383384// 右旋385privatevoid rightRotate(RBNode<T> node) {386if (node ==null) {387return;388        }389390final RBNode<T> p = node.parent;391392// 右旋. 应该认为 temp 不为null393final RBNode<T> temp = node.left;394         node.left = temp.right;395if (temp.right !=null) {396// 该结点可能不存在397             temp.right.parent = node;398        }399400         temp.right = node;401         node.parent = temp;402403// 旋转完成.修正根结点与父结点404// 1.node为根结点405if (node == root) {406             root = temp;407             temp.parent =null;408return;409        }410411// 2.node不为根结点412// node 为父结点的右孩子413if (node == p.right) {414             p.right = temp;415         }else {416             p.left = temp;417        }418         temp.parent = p;419    }420421// 中序遍历422publicvoid infixOrder() {423this.infixOrder(root);424    }425426privatevoid infixOrder(RBNode<T> root) {427if (root !=null) {428this.infixOrder(root.left);429             System.out.print("-->" + root.value + ":" + (root.red ? "红" : "黑"));430this.infixOrder(root.right);431        }432    }433434/**435     * 红黑树 树结点结构436*/437protectedstaticclass RBNode<Textends Comparable<T>> {438private T value;439// 默认为 红色 结点440privateboolean red =true;441442private RBNode<T> left;443private RBNode<T> right;444private RBNode<T> parent;445446public RBNode() {447        }448449public RBNode(T value) {450this.value = value;451        }452453// 返回结点的度454publicint getDegree() {455if (this.left ==null &&this.right ==null) {456return 0;457            }458459if ((this.left !=null &&this.right ==null) || (this.left ==null &&this.right !=null)) {460return 1;461            }462463return 2;464        }465466public RBNode<T> getUncle() {467final RBNode<T> grandFather =this.parent.parent;468final RBNode<T> parent =this.parent;469470if (parent == grandFather.left) {471return grandFather.right;472            }473474if (parent == grandFather.right) {475return grandFather.left;476            }477478returnnull;479        }480481public RBNode<T> getGrandFather() {482returnthis.parent.parent;483        }484485public RBNode<T> getBrother() {486final RBNode<T> p =this.parent;487488returnthis == p.left ? p.right : p.left;489        }490491        @Override492public String toString() {493return "RBNode{" +494                     "value=" + value +495                     ", red=" + red +496                     '}';497        }498    }499 }

完整的红黑树插入及删除

  代码示例:测试

 1publicclass Main { 2publicstaticvoid 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  用于读者验证上述代码的结果。上述测试案例构建的红黑树为:

数据结构与算法红黑树详解

  • 作者:Craftsman-L
  • 原文链接:https://www.cnblogs.com/originator/p/16065705.html
    更新时间:2022年5月31日10:19:55 ,共 10450 字。