上一章写到了红黑树的插入,今天记录下红黑树的删除方法,因为删除方法比插入更麻烦,情况更多,顾单独写一章。
有关红黑树的插入请看我这篇博客:https://blog.csdn.net/CSDN_LICY/article/details/104806459
有关AVL的插入与删除请看这篇博客:https://blog.csdn.net/CSDN_LICY/article/details/104735594
一、删除的几种情况
假设删除节点为D,一下均是D在左子树的分析情况,D在右子树,均是镜面对称操作。
二、删除代码
public void remove(int element) {
removeForBalance(element, root, true);
deleteElement(element, root);
}
public void removeForBalance(int element, RedBlackNode redBlackNode, boolean isRemove) {
if (redBlackNode == null) {
return;
}
if (element == root.element && root.left == null && root.right == null) {
root = null;
return;
}
int compareResult = element - redBlackNode.element;
if (compareResult < 0) {
removeForBalance(element, redBlackNode.left, isRemove);
} else if (compareResult > 0) {
removeForBalance(element, redBlackNode.right, isRemove);
} else if (redBlackNode.left != null && redBlackNode.right != null && isRemove) {
//当要删除的节点左右节点都不为空,则找到他的直接后继节点,来替换当前节点,最后再删除后继节点
redBlackNode.element = findMin(redBlackNode.right).element;
removeForBalance(redBlackNode.element, redBlackNode.right, isRemove);
} else {
parent = redBlackNode.parent;
//已经找到需要删除的节点,接下来进行删除,并恢复红黑树,假设待删除节点为D
//D为红节点只有一种情况;D为红节点,并且D为叶子节点
if (getColor(redBlackNode) == RED && redBlackNode.left == null && redBlackNode.right == null) {
return;
}
//D为黑节点,分为两种:1、D左节点或右节点为空,此时另一个节点必然为红节点。2、D为叶子节点
if (redBlackNode.left != null && isRemove) {
redBlackNode.element = redBlackNode.left.element;
redBlackNode.left = null;
return;
}
if (redBlackNode.right != null && isRemove) {
redBlackNode.element = redBlackNode.right.element;
redBlackNode.right = null;
return;
}
//下面讨论最复杂的情况:D为叶子节点,(每种情况都包含镜面对称情况)
if (redBlackNode.parent == null) {
redBlackNode.color = BLACK;
return;
}
if (redBlackNode == redBlackNode.parent.left) {
brother = parent.right;
if (getColor(brother) == RED) {
//第一种情况
removeByBrotherIsRed(true);
removeForBalance(redBlackNode.element, root, isRemove);
} else if (getColor(brother) == BLACK && getColor(brother.right) == RED) {
//第二种情况
removeByRemoteBrotherSon(true);
} else if (getColor(brother) == BLACK && getColor(brother.left) == RED) {
//第三种情况
removeByCloseBrotherSon(true);
} else if (getColor(parent) == RED && getColor(brother) == BLACK && getColor(brother.left) == BLACK && getColor(brother.right) == BLACK) {
if (brother.left == null && brother.right == null) {
//第四种情况
//父节点为红色,兄弟节点和两个侄子节点都是黑色,且两个侄子为空
parent.color = BLACK;
brother.color = RED;
} else if (brother.left != null && brother.right != null) {
//第五种情况
//父节点为红色,兄弟节点和两个侄子节点都是黑色,且两个侄子不为空
//父节点左单旋
leftRotate(parent);
}
} else if (getColor(parent) == BLACK && getColor(brother) == BLACK && getColor(brother.left) == BLACK && getColor(brother.right) == BLACK) {
//第六种情况
brother.color = RED;
removeForBalance(parent.element, root, false);
}
}
if (redBlackNode == redBlackNode.parent.right) {
brother = parent.left;
if (getColor(brother) == RED) {
//第一种情况 的镜面对称情况
removeByBrotherIsRed(false);
removeForBalance(redBlackNode.element, root, isRemove);
} else if (getColor(brother) == BLACK && getColor(brother.left) == RED) {
//第二种情况 的镜面对称情况
removeByRemoteBrotherSon(false);
} else if (getColor(brother) == BLACK && getColor(brother.right) == RED) {
//第三种情况 的镜面对称情况
removeByCloseBrotherSon(false);
} else if (getColor(parent) == RED && getColor(brother) == BLACK && getColor(brother.left) == BLACK && getColor(brother.right) == BLACK) {
if (brother.left == null && brother.right == null) {
//第四种情况 的镜面对称情况
//父节点为红色,兄弟节点和两个侄子节点都是黑色,且两个侄子为空
parent.color = BLACK;
brother.color = RED;
} else if (brother.left != null && brother.right != null) {
//第五种情况 的镜面对称情况
//父节点为红色,兄弟节点和两个侄子节点都是黑色,且两个侄子不为空
//父节点右单旋
rightRotate(parent);
}
} else if (getColor(parent) == BLACK && getColor(brother) == BLACK && getColor(brother.left) == BLACK && getColor(brother.right) == BLACK) {
//第六种情况
brother.color = RED;
removeForBalance(parent.element, root, false);
}
}
}
return;
}
/**
* 第一种情况:D的兄弟节点为红色。
* 思路:父节点和兄弟节点颜色互换,并且以父节点为当前节点进行旋转,变成情况四
* 当祖父节点存在,则让祖父节点指向新的父节点,否则,旋转后的节点为root。
* 此情况存在镜面对称情况,用isLeft区分,
* 当前节点是父节点的左节点,则isLeft为true,进行左单旋,否则进行右单旋。
*
* @param isLeft
*/
private void removeByBrotherIsRed(boolean isLeft) {
int parentColor = getColor(parent);
parent.color = getColor(brother);
brother.color = parentColor;
if (parent.parent != null) {
grand = parent.parent;
if (grand.left == parent) {
grand.left = isLeft ? leftRotate(parent) : rightRotate(parent);
} else {
grand.right = isLeft ? leftRotate(parent) : rightRotate(parent);
}
} else {
root = isLeft ? leftRotate(parent) : rightRotate(parent);
}
}
/**
* 第二种情况:D的远侄子为红色
* 思路,删除后右子树比左子树多一个黑节点,想办法让红节点变成黑节点,并让右子树两个黑节点分布到两个子树
* 1、父节点和兄弟节点互换颜色
* 2、远侄子变成黑色
* 3、对父节点进行RR操作(左单旋)
* isLeft为false则就是镜面对称情况,进行右单旋
* @param isLeft
*/
private void removeByRemoteBrotherSon(boolean isLeft) {
int parentColor = parent.color;
parent.color = brother.color;
brother.color = parentColor;
if (isLeft) {
//parent.left = null; //删除D
brother.right.color = BLACK;
} else {
//parent.right = null; //删除D
brother.left.color = BLACK;
}
if (parent.parent != null) {
grand = parent.parent;
if (grand.left == parent) {
grand.left = isLeft ? leftRotate(parent) : rightRotate(parent);
} else {
grand.right = isLeft ? leftRotate(parent) : rightRotate(parent);
}
} else {
root = isLeft ? leftRotate(parent) : rightRotate(parent);
}
}
/**
* 第三种情况:D的近侄子为红色
* 1、将兄弟节点和近侄子颜色互换
* 2、将兄弟节点右单旋,(此时兄弟节点和侄子角色互换)然后就变成了第二种情况
* 3、按照第二种情况进行删除
* isLeft为false则就是镜面对称情况,进行左单旋
* @param isLeft
*/
private void removeByCloseBrotherSon(boolean isLeft) {
int brotherColor = getColor(brother);
brother.color = getColor(brother.left);
if (isLeft) {
brother.left.color = brotherColor;
} else {
brother.right.color = brotherColor;
}
if (isLeft) {
parent.right = rightRotate(brother);
brother = parent.right;
} else {
parent.left = leftRotate(brother);
brother = parent.left;
}
removeByRemoteBrotherSon(isLeft);
}
private void deleteElement(int element, RedBlackNode redBlackNode) {
if (redBlackNode == null) {
return;
}
if (element == root.element && root.left == null && root.right == null) {
root = null;
return;
}
int compareResult = element - redBlackNode.element;
if (compareResult < 0) {
deleteElement(element, redBlackNode.left);
} else if (compareResult > 0) {
deleteElement(element, redBlackNode.right);
} else {
//找到删除节点直接删除
parent = redBlackNode.parent;
if (redBlackNode == parent.left) {
parent.left = null;
} else {
parent.right = null;
}
}
}
/**
* 找到最小节点
* @param redBlackNode
* @return
*/
private RedBlackNode findMin(RedBlackNode redBlackNode) {
if (redBlackNode == null) {
return null;
} else if (redBlackNode.left == null) {
return redBlackNode;
}
return findMin(redBlackNode.left);
}
三、测试结果
后记
关于二叉树的知识暂时就分享到这。
总的来说,自己收获很大,之前一直没搞懂红黑树,借此机会,也算是对自己一个交代。尤其对jdk8给hashMap增加红黑树有了更深的理解。
项目源码:
github:https://github.com/licy-IT/Tree.git
码云:https://gitee.com/rising-dragon/Tree.git