BST
package com.bin._2023._08.Red_blackTree;
/**
* @author liubi
*/
public class BsTree {
/**
* 使用递归进行查找
*/
}
class BsTreeNode<T extends Comparable<T>>{
/**
* 关键字(键值)
*/
T nodeKey;
/**
* 左节点引用
*/
BsTreeNode <T> left;
BsTreeNode <T> right;
/**
* 父节点的引用,进行节点删除的时候会用到
*/
BsTreeNode <T> parent;
public BsTreeNode() {
}
public BsTreeNode(T nodeKey) {
this.nodeKey = nodeKey;
}
@Override
public String toString() {
return "BsTreeNode{" +
"nodeKey=" + nodeKey +
'}';
}
public BsTreeNode<T> searchRecursive(BsTreeNode<T>root, T key){
if(root ==null){
return null;
}
if(root.nodeKey.compareTo(key)>0){
return searchRecursive(root.left,key);
}else if(root.nodeKey.compareTo(key)<0){
return searchRecursive(root.right,key);
}else{
return root;
}
}
public boolean insertRecursive(BsTreeNode<T>root, BsTreeNode<T> nodeInserted){
if(root ==null){
root=nodeInserted;
return true;
}
if(root.nodeKey.compareTo(nodeInserted.nodeKey)>0){
if(root.left==null){
root.left=nodeInserted;
nodeInserted.parent=root;
return true;
}else{
return insertRecursive(root.left,nodeInserted);
}
}else if(root.nodeKey.compareTo(nodeInserted.nodeKey)<0){
if(root.right==null){
root.right=nodeInserted;
nodeInserted.parent=root;
return true;
}else{
return insertRecursive(root.right,nodeInserted);
}
}else{
return false;
}
}
public void deleteRecursive(BsTreeNode<T> cur ,T key){
if(cur==null){
return ;
}
if(cur.nodeKey.compareTo(key)>0){
deleteRecursive(cur.left,key);
}
else if(cur.nodeKey.compareTo(key)<0){
deleteRecursive(cur.right,key);
}else{
//第一种:要删除的节点是叶子节点
if(cur.left==null&&cur.right==null){
BsTreeNode<T> parent = cur.parent;
//通过父节点删除节点
if(parent.left==cur){
parent.left=null;
}
if(parent.right==cur){
parent.right=null;
}
return;
}
//第二种:删除节点只有左子树
else if(cur.right==null){
BsTreeNode<T> rootLeft=cur.left;
cur.left=rootLeft.left;
cur.right=rootLeft.right;
cur.nodeKey=rootLeft.nodeKey;
//修正parent的引用
if(cur.left!=null){
cur.left.parent=cur;
}
if(cur.right!=null){
cur.right.parent=cur;
}
}
//第三种:删除节点只有右子树
else if(cur.left==null){
BsTreeNode<T> rootRight=cur.right;
cur.left=rootRight.left;
cur.right=rootRight.right;
cur.nodeKey=rootRight.nodeKey;
//修正parent的引用
if(cur.left!=null){
cur.left.parent=cur;
}
if(cur.right!=null){
cur.right.parent=cur;
}
}else{
//第四种:左右子树都不为空,用待删除节点的直接前驱,来代替待删除节点,同时在二叉排序树中对其直接前驱做删除操作
BsTreeNode<T> node=cur.left;
//找到当前节点的中序前驱节点
while(node.right!=null){
node=node.right;
}
//节点内容替换
cur.nodeKey=node.nodeKey;
//删除前驱节点
deleteRecursive(node,node.nodeKey);
}
}
}
}
缺点
在工程实践中不能使用朴素的BST,因为它存在一个致命的缺点——树结构容易不平衡,容易变成单边链表的情况,从而导致查询的时间会超过O(logN)
即便不出现这种极端情况,BST的结构也会在不断传入和删除节点过程中逐渐失衡,树结构越来越倾斜,这种结构不利于查询操作。
从而出现了AVL和红黑树,这是两种特殊的BST,通常使用AVL和红黑树就能达到很好的平衡效果,比如AVL,它除了具备BST的特性,还要求任一节点的左右子树高度差的绝对值不超过1,为了维持这个特性,AVL的节点插入和删除操作就伴随着对树结构的调整。
红黑树
概念
- 每个节点都有颜色,红色或者是黑色
- 根节点是黑色的
- 每个叶子节点都是黑色的
- 如果一个节点是红色的,则它的子节点必须是黑色
- 任意一个节点到该节点的每个叶子节点的所有路径上包含相同数目的黑色节点
其中的第五条确保:从任意节点出发到其叶子节点的所有路径中,最长路径的长度也不会超过最短路径的两倍,且指出每个节点的左右子树中黑色节点的层数是相等的,因此红黑树的黑色节点是完美平衡的。
在删除或增加红黑树的节点后,红黑树的结构会发生变化,需要采取旋转和节点百年色两种操作让树保持红黑树的特性
节点
public class RbTreeNode <T extends Comparable<T>>{
/**
* 黑色:true
* 红 false
*/
boolean color;
T nodeKey;
RbTreeNode<T>left;
RbTreeNode<T>right;
RbTreeNode<T>parent;
public RbTreeNode(boolean color, T nodeKey, RbTreeNode<T> left, RbTreeNode<T> right, RbTreeNode<T> parent) {
this.color = color;
this.nodeKey = nodeKey;
this.left = left;
this.right = right;
this.parent = parent;
}
}
树
class RbTree<T extends Comparable<T>>{
/**
* 根节点
*/
private RbTreeNode<T> mRoot;
private static final boolean RED=false;
private static final boolean BLACK=true;
public RbTree(){}
/**
* 获取某个节点的父节点
*/
private RbTreeNode<T> parentOf(RbTreeNode<T> node) {
return node!=null?node.parent:null;
}
/**
* 获取某个节点的颜色
*/
private boolean colorOf(RbTreeNode<T> node) {
return node!=null?node.color:BLACK;
}
/**
* 判断某个节点是否为红色
*/
private boolean isRed(RbTreeNode<T> node) {
return (node!=null)&&(node.color==RED)?true:false;
}
/**
* 判断某个节点是否为黑色
*/
private boolean isBlack(RbTreeNode<T> node) {
return !isRed(node);
}
/**
* 设置某个节点为黑色
*/
private void setBlack(RbTreeNode<T> node) {
if(node!=null){
node.color=BLACK;
}
}
/**
* 设置某个节点为红色
*/
private void setRed(RbTreeNode<T> node) {
if(node!=null){
node.color=RED;
}
}
/**
* 设置某个节点的颜色
*/
private void setColor(RbTreeNode<T> node,boolean color) {
if(node!=null){
node.color=color;
}
}
/**
* 设置某个节点的父节点
*/
private void setParent(RbTreeNode<T> node,RbTreeNode<T> parent) {
if(node!=null){
node.parent=parent;
}
}
}
旋转
左旋:以某个节点P作为支点,其右子节点V变为旋转节点P的父节点,右子节点V的左子节点R变为旋转节点P的右子节点,左子节点F保持不变
右旋:以某个节点P作为节点,其左子结点F变为旋转节点P的父节点,左子节点F的右子节点K变为旋转节点P的左子节点,右子结点V保持不变。
private void leftRotate(RbTreeNode<T> pNode){
//p的右子节点v变为p的父节点,v的左节点,变为p的右子节点
RbTreeNode<T> vNode=pNode.right;
RbTreeNode<T> rNode = vNode.left;
pNode.right=rNode;
if(rNode!=null){
rNode.parent=pNode;
}
vNode.parent= pNode.parent;
if(pNode.parent==null){
//如果p原来就没有parent,说明p就是根节点,从把V变成P的parent,更新根节点为v
this.mRoot=vNode;
}else{
//如果p原来就有parent,则V取代P作为这个parent的左孩子或有孩子
if(pNode.parent.left==pNode){
pNode.parent.left=vNode;
}else{
pNode.parent.right=vNode;
}
}
vNode.left=pNode;
pNode.parent=vNode;
}
private void rightRotate(RbTreeNode<T> pNode){
//P的左子节点F的右子节点K,变为P的左子节点
RbTreeNode<T> fNode = pNode.left;
RbTreeNode<T> kNode = fNode.right;
pNode.left=kNode;
if(kNode!=null){
kNode.parent=pNode;
}
fNode.parent=pNode.parent;
if(pNode.parent==null){
this.mRoot=fNode;
}else{
if(pNode.parent.left==pNode){
pNode.parent.left=fNode;
}else{
pNode.parent.right=fNode;
}
}
fNode.right=pNode;
pNode.parent=fNode;
}
插入节点
红黑树本身就是一颗二叉查找树,将节点插入后,该树依然是一棵二叉查找树。树的键值仍然是有序的。
新插入的节点必须为红色,这样才不会违背第五个特性(任意一个节点到该节点的每个叶子节点的所有路径上包含相同数目的黑色节点)
public boolean insertRecursively(RbTreeNode<T> root,RbTreeNode<T> nodeInserted){
if(root==null){
mRoot=nodeInserted;
return true;
}
if(root.nodeKey.compareTo(nodeInserted.nodeKey)>0){
if(root.left==null){
//1.将红黑树当作一个二叉查找树,将节点添加到二叉查找树中
root.left=nodeInserted;
nodeInserted.parent=root;
//2.将节点的颜色设置为红色
nodeInserted.color=RED;
//3.通过旋转和变色将它重新修正为一颗二叉查找树
insertFixUp(nodeInserted);
return true;
}else{
insertRecursively(root.left,nodeInserted);
}
}else if(root.nodeKey.compareTo(nodeInserted.nodeKey)<0){
if(root.right==null){
//1.将红黑树当作一个二叉查找树,将节点添加到二叉查找树中
root.right=nodeInserted;
nodeInserted.parent=root;
//2.将节点的颜色设置为红色
nodeInserted.color=RED;
//3.通过旋转和变色将它重新修正为一颗二叉查找树
insertFixUp(nodeInserted);
return true;
}else{
insertRecursively(root.right,nodeInserted);
}
}else{
return false;
}
return false;
}
private void insertFixUp(RbTreeNode<T> currentNode){
RbTreeNode<T> parent;
RbTreeNode<T> gparent;
//1:被插入的节点是根节点。处理方式:直接将此此节点涂为黑色,
if(currentNode==mRoot){
setBlack(mRoot);
return;
}
//2:被插入的节点的父节点是黑色。处理方式:不需要做操作
if(parentOf(currentNode)!=null&&isBlack(parentOf(currentNode))){
return;
}
//3.1:当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点,即叔叔节点是红色。
// 处理:1.将当前节点的父节点设为黑色
// 2.将当前节点的叔叔节点设为黑色
// 3.将当前节点的祖父节点设为红色。
// 4.将当前节点设置为当前节点,继续进行迭代操作
while(((parent=parentOf(currentNode))!=null)&&isRed(parent)){
gparent=parentOf(parent);
//若当前节点的父节点是祖父节点的左孩子
if(parent==gparent.left){
//当前节点的叔叔节点也是红色
RbTreeNode<T>uncle=gparent.right;
if((uncle!=null)&&isRed(uncle)){
setBlack(uncle);
setBlack(parent);
setRed(gparent);
currentNode=gparent;
continue;
}
//3.2:当前节点的叔叔节点是黑色,且当前节点是右孩子
// 处理:1.将当前节点的父节点作为新的当前节点
// 2.以新的当前节点,为支点进行左旋
// 3.根据新的当前节点所符合的情形继续操作
if(parent.right==currentNode){
RbTreeNode<T>tmp;
leftRotate(parent);
tmp=parent;
parent=currentNode;
currentNode=tmp;
}
//3.3:当前节点的叔叔节点是黑色,且当前节点是左孩子
// 处理:1.将当前节点的父节点设为黑色
// 2.将祖父节点设为红色
// 3.以祖父节点进行右旋
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
}
}
}
case 1
被插入的节点是根节点,直接将此节点涂为黑色
case 2
被插入的节点的父节点是黑色。处理方式:直接插入即可,不用其他操作
case 3
当前节点的父节点是红色。处理方式:该情况与红黑树的特性(如果一个节点是红色,则它的子节点必须是黑色的)相冲突。这种情况下一定存在非空祖父节点,也会存在叔叔节点,根据这个叔叔节点的情况,进一步分为三种情况
3.1
处理策略:
- 将当前节点的父节点(50)设为黑色
- 将当前节点的叔叔节点(70)设为黑色
- 将当前节点的祖父节点(60)设为红色
- 将当前节点的祖父节点(60)设为新的当前节点,之后继续对当前节点进行调整
3.2
当前节点(60)的父节点(40)是红色,叔叔节点(120)是黑色,且当前节点是其父节点的右孩子
处理策略:
- 将当前节点(60)的父节点(40)作为新的当前节点
- 以新的当前节点进行左旋
3.3
当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左孩子
删除节点
删除的框架代码
/**
* 删除键值为key的节点
*/
private void remove(RbTreeNode<T> nodeRemoved){
//被删除节点的左右孩子都不为空的情况
if((nodeRemoved.left!=null)&&(nodeRemoved.right!=null)){
removeNodeWithDoubleChild(nodeRemoved);
}
//被删除节点的左不为空的情况
else if((nodeRemoved.left!=null)){
removeNodeWithOnlyLeftChild(nodeRemoved);
}
//被删除节点的右孩子不为空的情况
else if((nodeRemoved.right!=null)){
removeNodeWithOnlyRightChild(nodeRemoved);
}
//被删除节点的左右孩子为空的情况
else{
removeNodeWithNoChild(nodeRemoved);
}
}
递归查询到待删除的节点
待删除节点有一个儿子:直接删除该nodeRemoved节点,然后用该节点的唯一子节点childOfNodeRemoved顶替它的位置
待删除节点有两个儿子:先找到它的中序后继节点,然后把它的中序后继节点内容复制给该节点nodeRemoved,之后删除它的中序后继节点。
private void removeNodeWithOnlyRightChild(RbTreeNode<T> nodeRemoved) {
RbTreeNode<T> childOfNodeRemoved=nodeRemoved.right;
RbTreeNode<T> parentOfNodeRemoved=nodeRemoved.parent;
boolean colorOfNodeRemoved=nodeRemoved.color;
childOfNodeRemoved.parent=parentOfNodeRemoved;
//被删除节点不是根节点(根节点不存在父节点)
if(parentOf(nodeRemoved)!=null){
if(parentOf(nodeRemoved).left==nodeRemoved){
parentOf(nodeRemoved).left=childOfNodeRemoved;
}else{
parentOf(nodeRemoved).right=childOfNodeRemoved;
}
}else{
//更新根节点
this.mRoot=childOfNodeRemoved;
}
//删除修正
//若被删除节点是红节点,不回破坏红黑树的性质,不需要修复
//只有当被删除的节点是黑节点才可能需要修复
if(colorOfNodeRemoved==BLACK){
removeFixUp(childOfNodeRemoved,parentOfNodeRemoved);
}
return;
}
private void removeNodeWithOnlyLeftChild(RbTreeNode<T> nodeRemoved) {
RbTreeNode<T> childOfNodeRemoved=nodeRemoved.left;
RbTreeNode<T> parentOfNodeRemoved=nodeRemoved.parent;
boolean colorOfNodeRemoved=nodeRemoved.color;
childOfNodeRemoved.parent=parentOfNodeRemoved;
//被删除节点不是根节点(根节点不存在父节点)
if(parentOf(nodeRemoved)!=null){
if(parentOf(nodeRemoved).left==nodeRemoved){
parentOf(nodeRemoved).left=childOfNodeRemoved;
}else{
parentOf(nodeRemoved).right=childOfNodeRemoved;
}
}else{
//更新根节点
this.mRoot=childOfNodeRemoved;
}
//删除修正
//若被删除节点是红节点,不回破坏红黑树的性质,不需要修复
//只有当被删除的节点是黑节点才可能需要修复
if(colorOfNodeRemoved==BLACK){
removeFixUp(childOfNodeRemoved,parentOfNodeRemoved);
}
return;
}
/**
* 被删除节点的左右孩子为空的情况
*/
private void removeNodeWithNoChild(RbTreeNode<T> nodeRemoved) {
RbTreeNode<T> childOfNodeRemoved=null;
RbTreeNode<T> parentOfNodeRemoved=nodeRemoved.parent;
boolean colorOfNodeRemoved=nodeRemoved.color;
//被删除节点不是根节点(根节点不存在父节点)
if(parentOf(nodeRemoved)!=null){
if(parentOf(nodeRemoved).left==nodeRemoved){
parentOf(nodeRemoved).left=childOfNodeRemoved;
}else{
parentOf(nodeRemoved).right=childOfNodeRemoved;
}
}else{
//更新根节点
this.mRoot=childOfNodeRemoved;
}
//删除修正
//若被删除节点是红节点,不回破坏红黑树的性质,不需要修复
//只有当被删除的节点是黑节点才可能需要修复
if(colorOfNodeRemoved==BLACK){
removeFixUp(childOfNodeRemoved,parentOfNodeRemoved);
}
return;
}
private void removeNodeWithDoubleChild(RbTreeNode<T> nodeRemoved) {
//找到当前节点的中序后继节点,
RbTreeNode<T>replace=nodeRemoved.right;
while(replace.left!=null){
replace=replace.left;
}
//拷贝替代节点的内容到被删除节点,这里只拷贝nodekey,实际上一个节点
//除了有nodeKey域,还有nodeData域,只不过完美简化了节点定义,拷贝过程应该连同nodeData一起拷贝
nodeRemoved.nodeKey=replace.nodeKey;
//然后删除替代节点
remove(replace);
return;
}
removeFixUp
假设当前节点childOfNodeRemoved 现在可以容纳两种颜色,如果它原来是红色,那么现在是红+黑,如果原来是黑色,那么它现在的颜色就是黑+黑,有了这重额外的黑色,原红黑树的性质5(任意一个节点到该节点的每个叶子节点的所有路径上包含相同数目的黑色节点)保持不变。现在只需要恢复其他性质就可以了,尽量向根移动和穷尽所有可能性。
- 现在childOfNodeRemoved 不仅包含它原本的颜色属性,还包含一个额外的黑色。违背了性质1(每个节点只有一个颜色,红色或黑色),此时的问题是由解决违反特性2、4、5变为了违反特性1、2、4
- removeFixUp的思想是:将childOfNodeRemoved(当前节点) 所包含的额外的黑色不断沿树上移动,直到出现下面的情况:
- childOfNodeRemoved 指向一个红+黑节点。将childOfNodeRemoved 设为一个黑节点即可
- childOfNodeRemoved 指向根,此时将childOfNodeRemoved设为一个黑节点既可
注意:childOfNodeRemoved (当前节点)在迭代调整的过程中会不断更新,因此每种case下的当前节点的位置可能是调整若干次之后的位置,且一次调整之后当前节点可能从一种case转向另一种case
对于当前节点的父节点进行左旋后,左旋后,为了保持红黑树特性,就需要在左旋前将当前节点的兄弟节点设为黑色,同时将当前节点的父节点设为红色。左旋后,由于当前节点的兄弟节点发送了变化,就需要重新设置当前节点的兄弟节点,进行下一步的迭代。
当前节点是黑+黑节点,我们将当前节点由黑+黑节变成黑节点,多余的一个黑属性移动到当前节点的父节点中,当前节点的父节点多出了一个黑属性,所有经过当前节点的分支中黑节点个数没变化。但是所有经过当前节点的兄弟节点的分支中黑色节点的个数增加了1,为了解决这个问题,我们需要将所有经过当前节点的兄弟节点的分支中黑色节点的个数减1,那么就通过将当前节点的兄弟节点设为红色。此时这个多余的颜色属性就到了当前节点的父节点中,将当前节点的父节点设为当前节点,进行处理迭代。
这几个步骤的目的是为了将REMOVE-FIXUP-CASE-3.3进行转换,转换成REMOVE-FIXUP-CASE-3.4,从而进行进一步的处理。转换的方式是对当前节点的兄弟节点进行右旋。为了保证右旋后,它仍然是红黑树,就需要在右旋前**“将当前节点的兄弟节点的左孩子设为黑色”,同时“将当前节点的兄弟节点设为红色”。右旋后,由于当前节点的兄弟节点发生了变化,需要更新当前节点的兄弟节点**,从而进行后续处理。 (进行下一步的选代)
我们需要对F进行左旋,左旋前需要调换F和B的颜色,并设置BRS为黑色,一位内左旋后,F和BLS是父子关系,我们已知BLS是红色,如果F是红色就违背了特性4(如果一个节点是红色,则它的子节点必须是黑色的),所有将F设置为黑色
但是,F设置为黑色后,为了保证满足红黑树特性5(任意一个节点到该节点的每个叶子节点的所有路径上包含相同数目的黑节点),所以左旋之后,第一:同时经过根节点和S的分支的黑色节点个数不变;第二:同时经过根节点和BLS的分支的黑色节点数不变;第三:同时经过根节点和BRS的分支的黑色节点数不变。
若要满足第一,只需要S丢弃它多余的颜色即可,因为S的颜色是黑+黑,在左旋后通过经过节点和S的分支的黑色节点个数加1,现在,只需要将S由黑+黑变为单独的黑 。
若要满足第二,只需要将F的原始颜色赋值给B即可,之前,我们已经将F设置为黑色。所以就调换一下F和B的颜色。在第二已经满足的条件下,要满足第三,只需要将BRS设置为黑色即可
经过上面的处理,红黑树的特性将全部得到满足。
private void removeFixUp(RbTreeNode<T> childOfNodeRemoved, RbTreeNode<T> parentOfNodeRemoved) {
//other是childOfNodeRemoved的兄弟节点
RbTreeNode<T>other;
//1 childOfNodeRemoved是红+黑节点
if((childOfNodeRemoved!=null)&&isRed(childOfNodeRemoved)){
setBlack(childOfNodeRemoved);
return;
}
//2childOfNodeRemoved是黑+黑节点 ,且 childOfNodeRemoved 是根,什么也不用做
if((childOfNodeRemoved!=null)&&isRed(childOfNodeRemoved)&&childOfNodeRemoved==this.mRoot){
return;
}
//3 当前的childOfNodeRemoved是黑+黑节点,且childOfNodeRemoved不是根
while(((childOfNodeRemoved)==null||isBlack(childOfNodeRemoved))&&(childOfNodeRemoved!=this.mRoot)){
if(parentOfNodeRemoved.left==childOfNodeRemoved){
other=parentOfNodeRemoved.right;
if(isRed(other)){
//3.1 childOfNodeRemoved的兄弟,other是红色
//将当前节点的兄弟节点设为黑色
//将当前节点的父节点设为红色
//对当前节点的父节点进行左旋
//左旋后,重新设置当前节点的兄弟节点
setBlack(other);
setRed(parentOfNodeRemoved);
leftRotate(parentOfNodeRemoved);
other=parentOfNodeRemoved.right;
}
if((other.left==null||isBlack(other.left))&&(other.right==null||isBlack(other.right))){
//3.2 childOfNodeRemoved的兄弟other是黑色,且other的两个孩子都是黑色
setRed(other);
childOfNodeRemoved=parentOfNodeRemoved;
parentOfNodeRemoved=parentOf(childOfNodeRemoved);
}else{
if(other.right==null||isBlack(other.right)){
//3.3 childOfNodeRemoved的兄弟other是黑色,并且other的左孩子是红色,右孩子是黑色
/**
* 1. 将当前节点的兄弟节点的左孩子设为黑色
* 2. 将当前节点的兄弟节点设为红色
* 3. 对当前节点的兄弟节点进行右旋
* 4. 右旋或,重新设置当前节点兄弟节点
*/
setBlack(other.left);
setRed(other);
rightRotate(other);
other=parentOfNodeRemoved.right;
}
//3.4 childOfNodeRemoved的兄弟other是黑色,并且other的右孩子是红色,左孩子是任意颜色
/**
* 1. 将当前节点的父节点颜色复制给当前节点的兄弟节点
* 2. 将当前节点的父节点设为黑色
* 3. 将当前节点的兄弟节点的右子节点设为黑色
* 4. 对当前节点的父节点进行左旋
* 5. 设置当前节点为根节点
* 6. 结束迭代
*/
setColor(other,colorOf(parentOfNodeRemoved));
setBlack(parentOfNodeRemoved);
setBlack(other.right);
leftRotate(parentOfNodeRemoved);
childOfNodeRemoved=this.mRoot;
break;
}
}else{
other=parentOfNodeRemoved.left;
if(isRed(other)){
//3.1 childOfNodeRemoved的兄弟,other是红色
//将当前节点的兄弟节点设为黑色
//将当前节点的父节点设为红色
//对当前节点的父节点进行左旋
//右旋后,重新设置当前节点的兄弟节点
setBlack(other);
setRed(parentOfNodeRemoved);
rightRotate(parentOfNodeRemoved);
other=parentOfNodeRemoved.left;
}
if((other.left==null||isBlack(other.left))&&(other.right==null||isBlack(other.right))){
//3.2 childOfNodeRemoved的兄弟other是黑色,且other的两个孩子都是黑色
setRed(other);
childOfNodeRemoved=parentOfNodeRemoved;
parentOfNodeRemoved=parentOf(childOfNodeRemoved);
}else{
if(other.left==null||isBlack(other.left)){
//3.3 childOfNodeRemoved的兄弟other是黑色,并且other的左孩子是红色,右孩子是黑色
/**
* 1. 将当前节点的兄弟节点的左孩子设为黑色
* 2. 将当前节点的兄弟节点设为红色
* 3. 对当前节点的兄弟节点进行右旋
* 4. 右旋或,重新设置当前节点兄弟节点
*/
setBlack(other.right);
setRed(other);
leftRotate(other);
other=parentOfNodeRemoved.left;
}
//3.4 childOfNodeRemoved的兄弟other是黑色,并且other的右孩子是红色,左孩子是任意颜色
/**
* 1. 将当前节点的父节点颜色复制给当前节点的兄弟节点
* 2. 将当前节点的父节点设为黑色
* 3. 将当前节点的兄弟节点的右子节点设为黑色
* 4. 对当前节点的父节点进行右旋
* 5. 设置当前节点为根节点
* 6. 结束迭代
*/
setColor(other,colorOf(parentOfNodeRemoved));
setBlack(parentOfNodeRemoved);
setBlack(other.left);
rightRotate(parentOfNodeRemoved);
childOfNodeRemoved=this.mRoot;
break;
}
}
}
}
总结
3-1
3-2
3-3
学习链接:https://leetcode.cn/circle/discuss/nsG69E/