建议先阅读普通二叉搜索树与平衡二叉搜索树的文章。理解一些基本的二叉树知识数据结构与算法【二叉搜索树】Java实现-CSDN博客
红黑树也是一种自平衡的二叉搜索树,较之 AVL,插入和删除时旋转次数更少。
首先介绍代码实现会用到的概念
所有节点都有两种颜色:红、黑⚫️
所有 null 视为黑色⚫️
红色节点不能相邻
根节点是黑色⚫️
从根到任意一个叶子节点,路径中的黑色⚫️节点数一样
根据该特性,我们可以总结出
红色节点要么没有孩子要么有两个黑孩子
以下情况均不属于红黑树
红红相邻
不满足从根节点到任意叶子节点路径中的黑色节点个数相同,到达1、3节点黑色节点个数为2,而到7、9的黑色节点个数为3。
当叶子节点不存在兄弟节点这种情况时。需要加入null值,而null值充当黑色节点。
因此,将该图补充完整后如下
节点2的叶子节点与其他叶子节点路径上的黑色个数不同。因此也不能称为红黑树。
但是下图就属于红黑树
这种情况下,即使将null值加上,也满足红黑树
public class RedBlackTree {
//需要红黑两种颜色
enum Color {
RED, BLACK;
}
Node root;
static class Node {
int key;
Object value;
Node left;
Node right;
Node parent; // 父节点
Color color = RED; // 颜色
public Node(int key, Object value) {
this.key = key;
this.value = value;
}
public Node(int key, Color color) {
this.key = key;
this.color = color;
}
public Node(int key, Color color, Node left, Node right) {
this.key = key;
this.color = color;
this.left = left;
this.right = right;
if (left != null) {
left.parent = this;
}
if (right != null) {
right.parent = this;
}
}
// 对于父结点来说,自己是否是左孩子
boolean isLeftChild() {
return parent != null && parent.left == this;
}
// 获取叔叔节点
Node uncle() {
//根节点与根节点的左右孩子均不存在叔叔节点
if (parent == null || parent.parent == null) {
return null;
}
//如果父亲节点是左孩子
if (parent.isLeftChild()) {
//返回父亲节点的兄弟节点
return parent.parent.right;
} else {
return parent.parent.left;
}
}
// 获取兄弟
Node sibling() {
//根节点不存在兄弟节点
if (parent == null) {
return null;
}
//如果该节点是左孩子
if (this.isLeftChild()) {
//返回右孩子
return parent.right;
} else {
return parent.left;
}
}
}
// 判断红
boolean isRed(Node node) {
return node != null && node.color == RED;
}
// 判断黑
boolean isBlack(Node node) {
return node == null || node.color == BLACK;
}
//与平衡二叉搜索树的旋转相比,多了一步修改各个节点的parent属性修改
private void rightRotate(Node node) {
//被旋转节点的父结点
Node parent = node.parent;
//获取新的子树父结点
Node newNode = node.left;
//将新的子树父结点的右孩子充当旋转节点的左孩子,腾出新父节点的右孩子位置给被旋转节点
Node rightChild = newNode.right;
if (rightChild != null) {
//如果右孩子不是null,那么将右孩子的父结点设置为被旋转节点
rightChild.parent = node;
}
node.left = rightChild;
//将新的子树节点的右孩子设置为被旋转节点
newNode.right = node;
//将新父节点的parent属性设置为被旋转节点的parent
newNode.parent = parent;
//将被旋转节点的parent属性设置为新的父结点
node.parent = newNode;
//修改被旋转节点的父结点的孩子属性
if (parent == null) {
//说明被旋转的节点为根节点
root = newNode;
} else if (parent.left == node) {//如果被旋转节点是父结点的左孩子
//将新的左孩子设置为新的子树父结点
parent.left = newNode;
} else {
parent.right = newNode;
}
}
// 左旋
private void leftRotate(Node node) {
Node parent = node.parent;
Node newNode = node.right;
Node leftChild = newNode.left;
if (leftChild != null) {
leftChild.parent = node;
}
newNode.left = node;
newNode.parent = parent;
node.right = leftChild;
node.parent = newNode;
if (parent == null) {
root = newNode;
} else if (parent.left == node) {
parent.left = newNode;
} else {
parent.right = newNode;
}
}
}
展示一下右旋的流程
旋转不需要进行变色,只需要修改移动节点的parent属性以及left或是right属性。
接下来针对红黑树特性,实现插入和删除代码
/**
* 新增或更新
* 正常增、遇到红红不平衡进行调整
*
* @param key 键
* @param value 值
*/
public void put(int key, Object value) {
Node p = root;
Node parent = null;
//找到新增位置与父结点位置
while (p != null) {
parent = p;
if (key < p.key) {
p = p.left;
} else if (p.key < key) {
p = p.right;
} else {
p.value = value; // 更新
return;
}
}
Node inserted = new Node(key, value);
if (parent == null) {
//说明向根节点更新数据
root = inserted;
} else if (key < parent.key) {
parent.left = inserted;
inserted.parent = parent;
} else {
parent.right = inserted;
inserted.parent = parent;
}
//新增完成后,进行修正红黑树
fixRedRed(inserted);
}
修正红黑树方法fixRedRed()存在四种情况:
首先需要知道的是插入节点均视为红色
case 1:插入节点为根节点,将根节点变黑⚫️
case 2:插入节点的父亲若为黑色⚫️,树的红黑性质不变,无需调整
插入节点的父亲为红色,触发红红相邻,红红相邻又分为case 3与case 4两种情况
case 3:叔叔为红色
父亲变为黑色⚫️,为了保证黑色平衡,连带的叔叔也变为黑色⚫️
祖父如果是黑色不变,会造成这颗子树黑色过多,因此祖父节点变为红色
祖父如果变成红色,可能会接着触发红红相邻,因此对将祖父进行递归调整
图示如下
接下来需要插入节点 1
触发红红相邻,且叔叔节点 4 也为红色
此时,不满足从根节点到任意叶子节点路径上黑色节点个数相同的条件,因此,需要把祖父节点 3变成红色
此时又触发了红红相邻,因此再次执行相同操作。
到达根节点时,将根节点变色就是实现了红黑树调整
case 4:叔叔为黑色⚫️
1、父亲为左孩子,插入节点也是左孩子,此时即 LL 不平衡
2、父亲为左孩子,插入节点是右孩子,此时即 LR 不平衡
父亲左旋,变成 LL 情况,按 1. 来后续处理
3、父亲为右孩子,插入节点也是右孩子,此时即 RR 不平衡
让父亲变黑⚫️,为了保证这颗子树黑色不变,将祖父变成红,但叔叔子树少了一个黑色
祖父左旋,补齐一个黑色给叔叔,父亲旋转上去取代祖父,由于它是黑色,不会再次触发红红相邻
4、父亲为右孩子,插入节点是左孩子,此时即 RL 不平衡
图示如下
接下来去添加节点2
触发红红相邻,经过调整后如下图所示
此时不平衡,需要进行一次右旋,旋转后结果如下
private void fixRedRed(Node x) {
// case 1 插入节点是根节点,变黑即可
if (x == root) {
x.color = BLACK;
return;
}
// case 2 插入节点父亲是黑色,无需调整
if (isBlack(x.parent)) {
return;
}
// case 3 当红红相邻,叔叔为红时
// 需要将父亲、叔叔变黑、祖父变红,然后对祖父做递归处理
Node parent = x.parent;
Node uncle = x.uncle();
Node grandparent = parent.parent;
if (isRed(uncle)){
parent.color = BLACK;
uncle.color = BLACK;
grandparent.color = RED;
//如果祖父与祖父的父亲也触发了红红相邻,那么递归修改祖父,直到不再触发红红相邻
fixRedRed(grandparent);
return;
}
// case 4 当红红相邻,叔叔为黑时
if (parent.isLeftChild() && x.isLeftChild()) { // LL
parent.color = BLACK;
grandparent.color = RED;
rightRotate(grandparent);
} else if (parent.isLeftChild()) { // LR
leftRotate(parent);
x.color = BLACK;
grandparent.color = RED;
rightRotate(grandparent);
} else if (!x.isLeftChild()) { // RR
parent.color = BLACK;
grandparent.color = RED;
leftRotate(grandparent);
} else { // RL
rightRotate(parent);
x.color = BLACK;
grandparent.color = RED;
leftRotate(grandparent);
}
}
删之前我们需要清楚红黑树一个特性:删黑色会失衡,删红色不会失衡
一共存在下面几种情况:
一;如果删除的是叶子节点
如果是红色节点,直接删除就好
case0:如果删除节点有两个孩子
图示如下
比如说要删除节点 8,那么找到 8 的后继节点 9,并交换 8 与 9 的key与value
接下来就相当于删除叶子节点了。
case 1:
删完了,直接将 root = null
用剩余节点替换了根节点的 key,value,根节点孩子 = null,颜色保持黑色⚫️不变
case 2:删的是黑⚫️,剩下的是红,剩下这个红节点变黑⚫️
图示如下
节点 3 顶替节点 2 的位置,但此时违背从根节点到叶子节点的黑色节点个数相同。因此,需要将剩下的这个节点修改为黑色
调整节点和剩下节点都是黑⚫️,触发双黑,双黑意思是,少了一个黑
case 3:被调整节点的兄弟为红,此时两个侄子定为黑 ⚫️
删除节点是左孩子,父亲左旋
删除节点是右孩子,父亲右旋
父亲和兄弟要变色,保证旋转后颜色平衡
旋转的目的是让黑侄子变为删除节点的黑兄弟,对删除节点再次递归,进入 case 4 或 case 5
图示如下
接下来要删除节点 4。父亲节点 6 左旋。
旋转过后颜色不平衡,需要修改被删除节点的原兄弟节点 8 与原父结点 6 的颜色。
此时删除节点 4 时,仍然会触发双黑,但是此时触发双黑走的是另一个逻辑case4或case5
case 4:被调整节点的兄弟为黑⚫️,两个侄子都为黑 ⚫️
将兄弟变红,目的是将删除节点和兄弟那边的黑色高度同时减少 1
如果父亲是红,则需将父亲变为黑,避免红红,此时路径黑节点数目不变
如果父亲是黑⚫️,说明这条路径还是少黑,再次让父节点触发双黑
当父结点为红色的情况,图示如下
将被调整节点 4 的兄弟节点 7 变色为红色,但此时节点6,7不调整的话触发双红,因此需要将父结点修改为黑色
修改过后,可以直接将删除节点去除。
以上情况是父结点为红色的情况,如果父结点为黑色,那么调整父结点颜色也无法使红黑树平衡。下面是父结点为黑色的情况。
需要删除节点 1。将兄弟节点 3 变红。但时父结点为黑色节点,那么将父结点作为调整节点再次执行双黑代码
被调整节点 2 仍满足case4的情况,因此将兄弟节点修改为红色。但父结点 4 依然是黑色节点,那么将父结点 4 作为新的被调整节点,执行双黑代码
此时被调整节点 4 仍满足case4的情况,因此将兄弟节点修改为红色。虽然父结点8仍然是黑色节点,但由于已经是根节点,因此结束触发双黑的代码。最后调整结果如下
case 5:被调整节点的兄弟为黑⚫️,至少一个红侄子
如果兄弟是左孩子,左侄子是红,LL 不平衡
将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️,平衡起见,左侄子也是黑⚫️
原来兄弟要成为父亲,需要保留父亲颜色
如果兄弟是左孩子,右侄子是红,LR 不平衡
将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️
右侄子会取代原来父亲,因此它保留父亲颜色
兄弟已经是黑了⚫️,无需改变
如果兄弟是右孩子,右侄子是红,RR 不平衡
将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️,平衡起见,右侄子也是黑⚫️
原来兄弟要成为父亲,需要保留父亲颜色
如果兄弟是右孩子,左侄子是红,RL 不平衡
将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️
左侄子会取代原来父亲,因此它保留父亲颜色
兄弟已经是黑了⚫️,无需改变
接下来查看一种LL的情况,图示如下
首先要删除的节点为 4。删除后LL不平衡,因此对节点 3 进行一次右旋。
旋转过后,需要对节点颜色进行修改,首先是原来的兄弟节点 2 ,修改为原来的父亲节点 3 的颜色。新的两个孩子节点1 ,3修改为黑色。
再来看一种LR的情况,图示如下
要删除节点 4 ,需要将兄弟节点 1 进行一次左旋。
然后再将父结点 3 进行一次右旋
旋转过后,可以看到,原本兄弟节点 1 的右孩子 2 变成了新的父结点,因此,需要将 2 的颜色修改为原本的父结点 3 的颜色,将原本的父结点 3 的颜色修改为黑色。
具体实现代码如下
public void remove(int key) {
//得到被删除节点
Node deleted = find(key);
if (deleted == null) {
return;
}
doRemove(deleted);
}
private void doRemove(Node deleted) {
//替代被删除节点的节点
Node replaceNode = findReplaced(deleted);
Node parent = deleted.parent;
//首先进行判断,如果要删除的节点是叶子节点
if (replaceNode == null) {
//如果是根节点
if (deleted == root) {
root = null;
} else {
if (isBlack(deleted)) {
//需要进行调整
fixDoubleBlack(deleted);
}
//如果不是根节点,判断是父结点的左孩子还是右孩子
if (deleted.isLeftChild()) {
parent.left = null;
} else {
parent.right = null;
}
}
return;
}
//如果被删除节点存在一个孩子
if (deleted.left == null || deleted.right == null) {
if (deleted == root) {
//如果是根节点,那么让该子节点直接顶替root节点即可
root.key = replaceNode.key;
root.value = replaceNode.value;
replaceNode.parent = root.left = root.right = null;
} else {
if (deleted.isLeftChild()) {
//如果被删除节点是父结点的左孩子
parent.left = replaceNode;
} else {
parent.right = replaceNode;
}
replaceNode.parent = parent;
deleted.left = deleted.parent = deleted.right = null;
//将被删除节点删除后,判断是否需要进行调整
if (isBlack(deleted) && isBlack(replaceNode)) {
//如果删除节点与后驱节点都为黑色,那么删除一个黑色会导致黑色节点个数不同(这种情况不存在)
fixDoubleBlack(replaceNode);
} else {
//如果被删除节点与后驱节点是一红一黑,那么都只需要将后驱节点颜色设置为黑色即可
replaceNode.color = BLACK;
}
}
return;
}
//说明有两个孩子,replaceNode是该节点的后驱节点,这里我们采用值交换的方法来实现删除节点
int t = deleted.key;
deleted.key = replaceNode.key;
replaceNode.key = t;
Object value = deleted.value;
deleted.value = replaceNode.value;
replaceNode.value = value;
//经过交换后,此时被删除节点只有一个孩子或是没有孩子。再次执行删除操作
doRemove(replaceNode);
}
/**
* 触发双黑的调整
* @param node 被调整的节点
*/
private void fixDoubleBlack(Node node) {
if (node == root){
return;
}
//被调整节点的父亲节点
Node parent = node.parent;
//被调整节点的兄弟节点
Node sibling = node.sibling();
//case 3代码,目的是调整红黑树为case4或case5的情况
if (isRed(sibling)){
if (node.isLeftChild()){
leftRotate(parent);
}else {
rightRotate(parent);
}
parent.color = RED;
sibling.color =BLACK;
fixDoubleBlack(node);
return;
}
if (sibling!=null){
//case 4
if (isBlack(sibling.left) && isBlack(sibling.right)){
sibling.color = RED;
if (isRed(parent)){
parent.color = BLACK;
}else {
fixDoubleBlack(parent);
}
}else {
//case 5
// LL
if (sibling.isLeftChild() && isRed(sibling.left)) {
rightRotate(parent);
sibling.left.color = BLACK;
sibling.color = parent.color;
}
// LR
else if (sibling.isLeftChild() && isRed(sibling.right)) {
sibling.right.color = parent.color;
leftRotate(sibling);
rightRotate(parent);
}
// RL
else if (!sibling.isLeftChild() && isRed(sibling.left)) {
sibling.left.color = parent.color;
rightRotate(sibling);
leftRotate(parent);
}
// RR
else {
leftRotate(parent);
sibling.right.color = BLACK;
sibling.color = parent.color;
}
parent.color = BLACK;
}
}
}
private Node find(int key) {
Node p = root;
while (p != null) {
if (p.key > key) {
p = p.left;
} else if (key > p.key) {
p = p.right;
} else {
return p;
}
}
return null;
}
// 查找剩余节点或是后继节点
private Node findReplaced(Node deleted) {
if (deleted.left == null && deleted.right == null) {
return null;
}
if (deleted.left == null) {
return deleted.right;
}
if (deleted.right == null) {
return deleted.left;
}
Node s = deleted.right;
while (s.left != null) {
s = s.left;
}
return s;
}
public class RedBlackTree {
enum Color {
RED, BLACK;
}
Node root;
static class Node {
int key;
Object value;
Node left;
Node right;
Node parent; // 父节点
Color color = RED; // 颜色
public Node(int key, Object value) {
this.key = key;
this.value = value;
}
public Node(int key, Color color) {
this.key = key;
this.color = color;
}
public Node(int key, Color color, Node left, Node right) {
this.key = key;
this.color = color;
this.left = left;
this.right = right;
if (left != null) {
left.parent = this;
}
if (right != null) {
right.parent = this;
}
}
// 是否是左孩子
boolean isLeftChild() {
return parent != null && parent.left == this;
}
// 获取叔叔节点
Node uncle() {
//根节点与根节点的左右孩子均不存在叔叔节点
if (parent == null || parent.parent == null) {
return null;
}
//如果父亲节点是左孩子
if (parent.isLeftChild()) {
//返回父亲节点的兄弟节点
return parent.parent.right;
} else {
return parent.parent.left;
}
}
// 获取兄弟
Node sibling() {
//根节点不存在兄弟节点
if (parent == null) {
return null;
}
//如果该节点是左孩子
if (this.isLeftChild()) {
//返回右孩子
return parent.right;
} else {
return parent.left;
}
}
}
// 判断红
boolean isRed(Node node) {
return node != null && node.color == RED;
}
// 判断黑
boolean isBlack(Node node) {
return node == null || node.color == BLACK;
}
//需要处理的是,被旋转节点的孩子节点的parent属性修改,以及被旋转节点的父结点的孩子属性修改
private void rightRotate(Node node) {
//被旋转节点的父结点
Node parent = node.parent;
//获取新的子树父结点
Node newNode = node.left;
//将新的子树父结点的右孩子充当旋转节点的左孩子,腾出新父节点的右孩子位置给被旋转节点
Node rightChild = newNode.right;
if (rightChild != null) {
//如果右孩子不是null,那么将右孩子的父结点设置为被旋转节点
rightChild.parent = node;
}
node.left = rightChild;
//将新的子树节点的右孩子设置为被旋转节点
newNode.right = node;
//将新父节点的parent属性设置为被旋转节点的parent
newNode.parent = parent;
//将被旋转节点的parent属性设置为新的父结点
node.parent = newNode;
//修改被旋转节点的父结点的孩子属性
if (parent == null) {
//说明被旋转的节点为根节点
root = newNode;
} else if (parent.left == node) {//如果被旋转节点是父结点的左孩子
//将新的左孩子设置为新的子树父结点
parent.left = newNode;
} else {
parent.right = newNode;
}
}
// 左旋
private void leftRotate(Node node) {
Node parent = node.parent;
Node newNode = node.right;
Node leftChild = newNode.left;
if (leftChild != null) {
leftChild.parent = node;
}
newNode.left = node;
newNode.parent = parent;
node.right = leftChild;
node.parent = newNode;
if (parent == null) {
root = newNode;
} else if (parent.left == node) {
parent.left = newNode;
} else {
parent.right = newNode;
}
}
/**
* 新增或更新
* 正常增、遇到红红不平衡进行调整
*
* @param key 键
* @param value 值
*/
public void put(int key, Object value) {
Node p = root;
Node parent = null;
//找到新增位置与父结点位置
while (p != null) {
parent = p;
if (key < p.key) {
p = p.left;
} else if (p.key < key) {
p = p.right;
} else {
p.value = value; // 更新
return;
}
}
Node inserted = new Node(key, value);
if (parent == null) {
//说明向根节点更新数据
root = inserted;
} else if (key < parent.key) {
parent.left = inserted;
inserted.parent = parent;
} else {
parent.right = inserted;
inserted.parent = parent;
}
//新增完成后,进行修正红黑树
fixRedRed(inserted);
}
private void fixRedRed(Node x) {
// case 1 插入节点是根节点,变黑即可
if (x == root) {
x.color = BLACK;
return;
}
// case 2 插入节点父亲是黑色,无需调整
if (isBlack(x.parent)) {
return;
}
// case 3 当红红相邻,叔叔为红时
// 需要将父亲、叔叔变黑、祖父变红,然后对祖父做递归处理
Node parent = x.parent;
Node uncle = x.uncle();
Node grandparent = parent.parent;
if (isRed(uncle)) {
parent.color = BLACK;
uncle.color = BLACK;
grandparent.color = RED;
//如果祖父与祖父的父亲也触发了红红相邻,那么递归修改祖父,直到不再触发红红相邻
fixRedRed(grandparent);
return;
}
// case 4 当红红相邻,叔叔为黑时
if (parent.isLeftChild() && x.isLeftChild()) { // LL
parent.color = BLACK;
grandparent.color = RED;
rightRotate(grandparent);
} else if (parent.isLeftChild()) { // LR
leftRotate(parent);
x.color = BLACK;
grandparent.color = RED;
rightRotate(grandparent);
} else if (!x.isLeftChild()) { // RR
parent.color = BLACK;
grandparent.color = RED;
leftRotate(grandparent);
} else { // RL
rightRotate(parent);
x.color = BLACK;
grandparent.color = RED;
leftRotate(grandparent);
}
}
public void remove(int key) {
//得到被删除节点
Node deleted = find(key);
if (deleted == null) {
return;
}
doRemove(deleted);
}
private void doRemove(Node deleted) {
//替代被删除节点的节点
Node replaceNode = findReplaced(deleted);
Node parent = deleted.parent;
//首先进行判断,如果要删除的节点是叶子节点
if (replaceNode == null) {
//如果是根节点
if (deleted == root) {
root = null;
} else {
if (isBlack(deleted)) {
//需要进行调整
fixDoubleBlack(deleted);
}
//如果不是根节点,判断是父结点的左孩子还是右孩子
if (deleted.isLeftChild()) {
parent.left = null;
} else {
parent.right = null;
}
}
return;
}
//如果被删除节点存在一个孩子
if (deleted.left == null || deleted.right == null) {
if (deleted == root) {
//如果是根节点,那么让该子节点直接顶替root节点即可
root.key = replaceNode.key;
root.value = replaceNode.value;
replaceNode.parent = root.left = root.right = null;
} else {
if (deleted.isLeftChild()) {
//如果被删除节点是父结点的左孩子
parent.left = replaceNode;
} else {
parent.right = replaceNode;
}
replaceNode.parent = parent;
deleted.left = deleted.parent = deleted.right = null;
//将被删除节点删除后,判断是否需要进行调整
if (isBlack(deleted) && isBlack(replaceNode)) {
//如果删除节点与后驱节点都为黑色,那么删除一个黑色会导致黑色节点个数不同(这种情况不存在)
fixDoubleBlack(replaceNode);
} else {
//如果被删除节点与后驱节点是一红一黑,那么都只需要将后驱节点颜色设置为黑色即可
replaceNode.color = BLACK;
}
}
return;
}
//说明有两个孩子,replaceNode是该节点的后驱节点,这里我们采用值交换的方法来实现删除节点
int t = deleted.key;
deleted.key = replaceNode.key;
replaceNode.key = t;
Object value = deleted.value;
deleted.value = replaceNode.value;
replaceNode.value = value;
//经过交换后,此时被删除节点只有一个孩子或是没有孩子。再次执行删除操作
doRemove(replaceNode);
}
/**
* 触发双黑的调整
* @param node 被调整的节点
*/
private void fixDoubleBlack(Node node) {
if (node == root){
return;
}
//被调整节点的父亲节点
Node parent = node.parent;
//被调整节点的兄弟节点
Node sibling = node.sibling();
//case 3代码,目的是调整红黑树为case4或case5的情况
if (isRed(sibling)){
if (node.isLeftChild()){
leftRotate(parent);
}else {
rightRotate(parent);
}
parent.color = RED;
sibling.color =BLACK;
fixDoubleBlack(node);
return;
}
if (sibling!=null){
//case 4
if (isBlack(sibling.left) && isBlack(sibling.right)){
sibling.color = RED;
if (isRed(parent)){
parent.color = BLACK;
}else {
fixDoubleBlack(parent);
}
}else {
//case 5
// LL
if (sibling.isLeftChild() && isRed(sibling.left)) {
rightRotate(parent);
sibling.left.color = BLACK;
sibling.color = parent.color;
}
// LR
else if (sibling.isLeftChild() && isRed(sibling.right)) {
sibling.right.color = parent.color;
leftRotate(sibling);
rightRotate(parent);
}
// RL
else if (!sibling.isLeftChild() && isRed(sibling.left)) {
sibling.left.color = parent.color;
rightRotate(sibling);
leftRotate(parent);
}
// RR
else {
leftRotate(parent);
sibling.right.color = BLACK;
sibling.color = parent.color;
}
parent.color = BLACK;
}
}
}
private Node find(int key) {
Node p = root;
while (p != null) {
if (p.key > key) {
p = p.left;
} else if (key > p.key) {
p = p.right;
} else {
return p;
}
}
return null;
}
// 查找剩余节点或是后继节点
private Node findReplaced(Node deleted) {
if (deleted.left == null && deleted.right == null) {
return null;
}
if (deleted.left == null) {
return deleted.right;
}
if (deleted.right == null) {
return deleted.left;
}
Node s = deleted.right;
while (s.left != null) {
s = s.left;
}
return s;
}
}