一、类及内部结点的定义
java
public class RedBlackTree<K extends Comparable<K>> {
private Node root;
private final static boolean RED = false;
private final static boolean BLACK = true;
class Node {
boolean color;
K key;
Node left;
Node right;
Node parent; //不使用递归,就需要记录父结点
public Node(boolean color, K key, Node left, Node right, Node parent) {
this.color = color;
this.key = key;
this.left = left;
this.right = right;
this.parent = parent;
}
}
}
二、插入元素
一直比较,先找到要插入的位置,再插入
java
public void insert(K key) {
if (key == null) throw new IllegalArgumentException("param is null");
insert(new Node(RED, key, null, null, null));
}
public void insert(Node newNode) {
Node mid = root; //遍历节点
Node midF = null; //待插入结点父节点
int com = 0;
//找到待插入结点的父节点
while (mid != null) {
midF = mid;
com = newNode.key.compareTo(midF.key);
if (com < 0) mid = mid.left;
if (com >= 0) mid = mid.right;
}
newNode.parent = midF;
//判断父节点是否为空
if (midF != null) { //非空
//比较新结点与父节点的大小,判断是插入到左边还是右边
if (com < 0) midF.left = newNode;
if (com >= 0) midF.right = newNode;
} else { //空,也就是root = null;
root = newNode;
}
//修正
insertFixUp(newNode);
}
三、插入后修正
理论上插入后有五种情况:
3.1 根结点为空,直接插入作为根结点
不需要修正
3.2 插入结点的父结点为黑色,直接插入
不需要修正
3.3 插入结点的父亲结点结点为红色
需要修正
3.3.1 叔叔结点为红色
处理方法:直接变色
Exp1:"3"为新插入结点,变色
Exp2: "30"为新插入结点,变色
3.3.2 叔叔结点为黑色(nil结点) ,插入结点&父结点&爷爷结点在一条直线上
处理方法:插入结点的爷爷结点左旋或者右旋
Exp1: "3"为新插入结点,"10"右旋
Exp2: "3"为新插入结点,"1"左旋
3.3.3 叔叔结点为黑色(nil结点) ,插入结点&父结点&爷爷结点不在一条直线上(可以看作是三者构成一个三角形)
处理方法:插入结点的父亲结点左旋或者右旋,之后就回到了三者在一条直线上的情况,合并处理(上一种)
Exp1: "8"为新插入结点,父结点"5"左旋,之后回到上一中情况
Exp2: "15"为新插入结点,父结点"20"左旋,之后回到上一中情况
java
private void insertFixUp(Node node) {
//新插入结点的父节点与祖父节点
Node parent;
Node grandParent;
//新插入结点的父节点非空为红色时,才需要修正
while ((parent = node.parent) != null && parent.color == RED) {
grandParent = parent.parent;
//如果父节点是爷爷结点左孩子
if (grandParent.left == parent) {
Node uncle = grandParent.right; //叔叔结点
//case1:叔叔结点为红色 变色即可
if (uncle != null && uncle.color == RED) {
parent.color = BLACK;
grandParent.color = RED;
uncle.color = BLACK;
//继续向上调整
node = grandParent;
//跳出本次循环
continue;
}
//case2:叔叔结点为黑色,且新结点为父节点的右孩子(要对父节点左旋变成case3)
//走到这,可能不存在叔叔结点,或者叔叔结点为黑色(nil)
if (parent.right == node) {
leftRotate(parent);
//更新parent
parent = parent.parent;
}
//case3:叔叔结点为黑色,且新结点为父节点的左孩子(变色右旋)
//走到这,新节点为左孩子
parent.color = BLACK;
parent.parent.color = RED;
rightRotate(parent.parent);
} else {
//如果父节点是爷爷结点右孩子
Node uncle = grandParent.left; //叔叔结点
//case1:叔叔结点为红色 变色即可
if (uncle != null && uncle.color == RED) {
parent.color = BLACK;
grandParent.color = RED;
uncle.color = BLACK;
//继续向上调整
node = grandParent;
//跳出本次循环
continue;
}
//case2:叔叔结点为黑色,且新结点为父节点的左孩子(要对父节点右旋变成case3)
if (parent.left == node) {
rightRotate(parent);
//更新parent!!!(旋转之后parent变换了位置)
parent = parent.parent; //parent = node(新插入的结点)
}
//case3:叔叔结点为黑色,且新结点为父节点的右孩子(变色左旋)
parent.color = BLACK;
parent.parent.color = RED;
leftRotate(parent.parent);
}
}
//新插入结点父节点为空,说明插入到了根节点
root.color = BLACK;
}
四、左旋/右旋
左右旋只研究一种情况即可,假如我们研究左旋。
左旋只分为两种情况:
4.1 插入结点&父结点&爷爷结点在一条直线上
Exp1:旋转结点是根结点,旋转"10"
Exp2:旋转结点不是根结点,旋转"20"
4.2 插入结点&父结点&爷爷结点不在一条直线上
Exp:旋转"5"
java
//写这部分代码,画图就好理解了
private void leftRotate(Node h) {
Node son = h.right; //目标结点的右孩子
h.right = son.left; //设置旋转之后目标结点的右孩子
if(son.left != null) son.left.parent = h; //设置son.left的父节点
son.parent = h.parent; //设置son结点的父节点
if(h.parent == null){ //若原来目标结点父节点为null(根节点),将son设为根节点
root = son;
}else {
//走到这里,原来目标结点一定有父节点
//判断h是父节点的左孩子还是右孩子,并设置其孩子
if(h.parent.left == h){
h.parent.left = son;
}else {
h.parent.right = son;
}
}
//设置son的左孩子
son.left = h;
//最后将原来目标结点的父节点设置为son
h.parent = son;
}
private void rightRotate(Node h) {
Node son = h.left; //目标结点的左孩子
h.left = son.right; //设置旋转之后目标结点的左孩子
if(son.right != null) son.right.parent = h; //设置son.right的父节点
son.parent = h.parent; //设置son结点的父节点
if(h.parent == null){ //若原来目标结点父节点为null(根节点),将son设为根节点
root = son;
}else {
//走到这里,原来目标结点一定有父节点
//判断h是父节点的左孩子还是右孩子,并设置其孩子
if(h.parent.left == h){
h.parent.left = son;
}else {
h.parent.right = son;
}
}
//设置son的左孩子
son.right = h;
//最后将原来目标结点的父节点设置为son
h.parent = son;
}
五、删除元素
删除结点需要找到替换结点
中序遍历的删除结点的前驱(删除结点左孩子的最右结点)或者后继(删除结点右孩子的最左结点)
-
删除元素无子结点时,删除结点可能为红色或者黑色
- 若删除结点为红色,直接删除即可
- 若删除结点为黑色,删除后需要修正(最麻烦部分)
-
删除结点只有一个子结点 ,那么删除结点只能为黑色,其子结点只能为红色(否则无法满足红黑树的性质),此时使用子结点作为替换结点即可,把替换结点(删除结点的孩子结点)置为黑色即可
-
删除结点的左右子树都不空,使用中序遍历的后继结点作为替换结点(即删除元素右子树的最左节点),之后就转化为上面两种情况
java
public void remove(K key) {
Node node = get(root, key);
if (node != null) remove(node);
}
private void remove(Node node) {
if (node == null) return;
Node replaceNode; //删除结点的代替结点
// Case1:左右子树都不空
if (node.left != null && node.right != null) {
//找到代替节点(中序遍历的后继节点,即右子树的最左结点)
replaceNode = node; //遍历结点的父结点(最后会指向替换结点)
Node mid = node.right; //遍历结点,遍历右子树的左节点
while (mid != null) {
replaceNode = mid;
mid = mid.left;
}
//找到代替结点,交换值,删除代替结点
K value = node.key;
node.key = replaceNode.key;
replaceNode.key = value;
remove(replaceNode);
return;
}
// Case2: 有一个孩子或者没有孩子
//找到替换结点(左孩子或者右孩子(右孩子可能为nil))
if (node.left != null) replaceNode = node.left;
else replaceNode = node.right;
//替换删除
Node parent = node.parent; //要删除结点的父结点
if (parent == null) {
//如果当前父结点为空(node为根结点)
root = replaceNode;
//不要忘记设置替换结点的parent
if (replaceNode != null) replaceNode.parent = null;
} else {
//如果当前父结点非空
if (replaceNode != null) replaceNode.parent = parent;
//设置删除结点父结点的子结点
if (parent.left == node) {
parent.left = replaceNode;
} else {
parent.right = replaceNode;
}
}
//若删除的叶子结点为黑色,则需要进行修正
if (node.color == BLACK) removeFix(parent, replaceNode);
}
六、删除后修正
删除动作(移除节点)之后,看看这个节点是不是黑色的叶子节点,如果不是,简单处理就可以达到平衡了;
先看删除结点的父结点是不是根节点,是的话什么都不用管;不是的话看兄弟什么颜色:
2.1 兄弟是红色:进行旋转变色,去到兄弟为黑色那里处理
2.2 兄弟是黑色,看看兄弟子节点是不是全部都是黑。
(1)全黑的话,无论父亲结点是红是黑,可以统一处理;
(2)不全黑,看兄在的位置,兄在左的话,看兄的左子是不是红色,进行对应处理;兄在右的话,看兄的右子是不是红色,进行对应处理。
6.1 删除结点的兄弟节点为红色
6.1.1 兄弟结点在左孩子(兄红-兄左)
修正方法:父结点右旋,交换父结点和兄弟结点颜色,兄弟结点置为父结点左孩子,转到兄弟结点为黑色的情况
Exp:删除结点"30",父结点"20"右旋,变色,此情况转到了2.1.1
删除结果:(上面最后一步20不平衡)
6.1.2 兄弟结点在右孩子(兄红-兄右)
修正方法:父结点左旋,交换父结点和兄弟结点颜色,兄弟结点置为父结点右孩子,转到兄弟结点为黑色的情况
Exp:删除结点"10",父结点"20"左旋,变色,兄弟结点置为"25",此情况转到了2.1.1
删除结果:
6.2 删除结点的兄弟节点为黑色
6.2.1 删除结点的兄弟子结点全黑(两种情况可以统一处理)
修正方法:父亲结点变红,父亲结点变为新的代替节点,继续递归
6.2.1.1 父亲结点为红色(兄黑-兄子全黑-父红)
Exp:删除"5","10"和"15"交换颜色
6.2.1.2 父亲结点为黑色(兄黑-兄子全黑-父黑)
Exp: 删除结点"10","30"变红 ( 找不到其他情况???)
6.2.2 删除结点的兄弟子结点不全黑
6.2.2.1兄弟节点是左孩子,并且兄弟结点左孩子红色(右孩子黑)(兄黑-兄子不全黑-兄左,兄左子红)
修正方法:父结点右旋,之后交换父结点和兄弟结点的颜色,最后兄弟结点左孩子置黑
Exp:删除结点"15",父节点"10"右旋,变色
6.2.2.2 兄弟节点是左孩子,并且兄弟结点左孩子黑色(右孩子红)(兄黑-兄子不全黑-兄左,兄左子黑)
修正方法:兄弟结点左旋,交换兄弟结点和其右孩子的颜色,转到 2.2.1
Exp:删除结点"8",兄弟结点"3"左旋,转到2.2.1
删除结果:
6.2.2.3 兄弟节点是右孩子,并且兄弟结点右孩子红色(兄黑-兄子不全黑-兄右,兄右子红)
修正方法:父结点左旋,之后交换父结点和兄弟结点的颜色,最后兄弟结点右孩子置黑
Exp:删除结点"3",父结点"5"左旋,变色
6.2.2.4 兄弟节点是右孩子,并且兄弟结点右孩子黑色(兄黑-兄子不全黑-兄右,兄右子黑)
修正方法:兄弟结点右旋,交换兄弟结点和其左孩子的颜色,转到 2.2.3
Exp:删除结点"5",兄弟结点"15"右旋,转到2.2.3
删除结果:
七、一个添加/删除例子
注意:N为要平衡的结点,S为兄弟结点
(1)删除50,用60替换,等同于删除只有一个孩子的节点,简单处理即可:
(2)删除70:
(兄红兄左)情况1.1
---> 平衡节点改变(兄黑-兄子不全黑-兄左,兄左子黑)情况 2.2.2
--->(兄黑-兄子不全黑-兄左,兄左子红)情况 2.2.1
(3)删除60:
(兄黑-兄子全黑)情况2.1
(4)删除10:
(兄黑-兄不全黑-兄右子黑)情况2.2.4
---> (兄黑-兄不全黑-兄右子红) 情况2.2.3
(5)删除20:
(兄黑-兄子全黑)情况2.1
java
private void removeFix(Node parent, Node replaceNode) {
//删除结点为叶结点才会进入循环,即 replaceNode == null,
// replaceNode != null 说明删除的结点有一个孩子,直接变成黑色即可
while ((replaceNode == null || replaceNode.color == BLACK) && root != replaceNode) {
//删除结点是左孩子
if (replaceNode == parent.left) {
//兄弟结点为父结点的右孩子
Node brother = parent.right;
//Case1:(兄弟红-兄右) 将父亲结点变红,兄弟节点变黑,父结点左旋
if (brother != null && brother.color == RED) {
brother.color = BLACK;
parent.color = RED;
leftRotate(parent);
brother = parent.right; //因为后面要交换兄弟节点和父结点颜色
}
//Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
if (brother == null || (brother.left == null && brother.right == null)
|| (brother.left != null && brother.right != null && brother.left.color == BLACK && brother.right.color == BLACK)) {
if (brother != null) brother.color = RED;
replaceNode = parent;
parent = parent.parent;
continue;
}
//Case3:(兄黑-兄子不全黑-兄右子黑) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
if (brother.left != null && brother.left.color == RED) {
brother.color = RED;
brother.left.color = BLACK;
rightRotate(brother);
//更新兄弟结点
brother = brother.parent;
}
//Case4:(兄黑-兄子不全黑-兄右子红) 交换父结点和兄弟结点颜色, 父节点左旋,最后兄右孩子置黑,跳出循环
brother.color = parent.color;
parent.color = BLACK;
brother.right.color = BLACK;
leftRotate(parent);
replaceNode = root;
} else { //删除结点是右孩子或者为nil
//兄弟结点为父结点的左孩子
Node brother = parent.left;
//Case1:(兄弟红-兄左) 交换父节点和兄弟结点颜色(父变红,兄变黑),父结点右旋
if (brother != null && brother.color == RED) {
brother.color = BLACK;
parent.color = RED;
rightRotate(parent);
brother = parent.left;
}
//Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
if (brother == null || (brother.left == null && brother.right == null)
|| (brother.left != null && brother.right != null && brother.left.color == BLACK && brother.right.color == BLACK)) {
if (brother != null) brother.color = RED;
replaceNode = parent;
parent = parent.parent;
continue;
}
//Case3:(兄黑-兄子不全黑-兄左子黑(nil),右子红) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
if (brother.right != null && brother.right.color == RED) {
brother.color = BLACK;
brother.right.color = BLACK;
leftRotate(brother);
//更新兄弟结点
brother = brother.parent;
}
//Case4:(兄黑-兄子不全黑-兄左子红,右子黑(nil)) 交换父结点和兄弟结点颜色, 父节点右旋,最后兄左孩子置黑, 跳出循环
brother.color = parent.color;
parent.color = BLACK;
brother.left.color = BLACK;
rightRotate(parent);
replaceNode = root;
}
}
//删除结点只有一个孩子,那么对替换结点改变颜色即可
if (replaceNode != null) replaceNode.color = BLACK;
}
八、查找结点
java
public Node get(K key) {
return get(root, key);
}
private Node get(Node node, K key) {
if (node == null) return null;
int com = key.compareTo(node.key);
if (com < 0) {
return get(node.left, key);
} else if (com > 0) {
return get(node.right, key);
} else {
return node;
}
}
九、代码
java
import lombok.Data;
/**
* @Author: fzhaaa
* @Date: 2022/5/25 8:44
* @Description:
*/
@Data
public class RedBlackTree<K extends Comparable<K>> {
private Node root;
private final static boolean RED = false;
private final static boolean BLACK = true;
/**
* 插入key
*
* @param key
* @return
*/
public void insert(K key) {
if (key == null) throw new IllegalArgumentException("param is null");
insert(new Node(RED, key, null, null, null));
}
/**
* 新建一个结点插入到红黑树中
*
* @param newNode
* @return
*/
public void insert(Node newNode) {
Node mid = root; //遍历节点
Node midF = null; //待插入结点父节点
int com = 0;
//找到待插入结点的父节点
while (mid != null) {
midF = mid;
com = newNode.key.compareTo(midF.key);
if (com < 0) mid = mid.left;
if (com >= 0) mid = mid.right;
}
newNode.parent = midF;
//判断父节点是否为空
if (midF != null) { //非空
//比较新结点与父节点的大小,判断是插入到左边还是右边
if (com < 0) midF.left = newNode;
if (com >= 0) midF.right = newNode;
} else { //空,也就是root = null;
root = newNode;
}
//修正
insertFixUp(newNode);
}
/**
* 红黑树插入修正
*
* @param node 新插入的节点
*/
private void insertFixUp(Node node) {
//新插入结点的父节点与祖父节点
Node parent;
Node grandParent;
//新插入结点的父节点非空为红色时,才需要修正
while ((parent = node.parent) != null && parent.color == RED) {
grandParent = parent.parent;
//如果父节点是爷爷结点左孩子
if (grandParent.left == parent) {
Node uncle = grandParent.right; //叔叔结点
//case1:叔叔结点为红色 变色即可
if (uncle != null && uncle.color == RED) {
parent.color = BLACK;
grandParent.color = RED;
uncle.color = BLACK;
//继续向上调整
node = grandParent;
//跳出本次循环
continue;
}
//case2:叔叔结点为黑色,且新结点为父节点的右孩子(要对父节点左旋变成case3)
//走到这,可能不存在叔叔结点,或者叔叔结点为黑色(nil)
if (parent.right == node) {
leftRotate(parent);
//更新parent
parent = parent.parent;
}
//case3:叔叔结点为黑色,且新结点为父节点的左孩子(变色右旋)
//走到这,新节点为左孩子
parent.color = BLACK;
parent.parent.color = RED;
rightRotate(parent.parent);
} else {
//如果父节点是爷爷结点右孩子
Node uncle = grandParent.left; //叔叔结点
//case1:叔叔结点为红色 变色即可
if (uncle != null && uncle.color == RED) {
parent.color = BLACK;
grandParent.color = RED;
uncle.color = BLACK;
//继续向上调整
node = grandParent;
//跳出本次循环
continue;
}
//case2:叔叔结点为黑色,且新结点为父节点的左孩子(要对父节点右旋变成case3)
if (parent.left == node) {
rightRotate(parent);
//更新parent!!!(旋转之后parent变换了位置)
parent = parent.parent; //parent = node(新插入的结点)
}
//case3:叔叔结点为黑色,且新结点为父节点的右孩子(变色左旋)
parent.color = BLACK;
parent.parent.color = RED;
leftRotate(parent.parent);
}
}
//新插入结点父节点为空,说明插入到了根节点
root.color = BLACK;
}
/**
* 删除元素,提供给外界使用
*
* @param key
*/
public void remove(K key) {
Node node = get(root, key);
if (node != null) remove(node);
}
private void remove(Node node) {
if (node == null) return;
Node replaceNode; //删除结点的代替结点
if (node.left != null && node.right != null) {
//找到代替节点(中序遍历的后继节点,即右子树的最左结点)
replaceNode = node; //遍历结点的父结点(最后会指向替换结点)
Node mid = node.right; //遍历结点,遍历右子树的左节点
while (mid != null) {
replaceNode = mid;
mid = mid.left;
}
//找到代替结点,交换值,删除代替结点
K value = node.key;
node.key = replaceNode.key;
replaceNode.key = value;
remove(replaceNode);
return;
}
// Case2: 有一个孩子或者没有孩子
//找到替换结点(左孩子或者右孩子(右孩子可能为nil))
if (node.left != null) replaceNode = node.left;
else replaceNode = node.right;
//替换删除
Node parent = node.parent; //要删除结点的父结点
if (parent == null) {
//如果当前父结点为空(node为根结点)
root = replaceNode;
//不要忘记设置替换结点的parent
if (replaceNode != null) replaceNode.parent = null;
} else {
//如果当前父结点非空
if (replaceNode != null) replaceNode.parent = parent;
//设置删除结点父结点的子结点
if (parent.left == node) {
parent.left = replaceNode;
} else {
parent.right = replaceNode;
}
}
//若删除的叶子结点为黑色,则需要进行修正
if (node.color == BLACK) removeFix(parent, replaceNode);
}
/**
* 删除后修正
* 删除黑色叶子结点后发生的操作
*
* @param parent 替代节点的父结点
* @param replaceNode 替代节点(删除结点)
*/
private void removeFix(Node parent, Node replaceNode) {
//删除结点为叶结点才会进入循环,即 replaceNode == null,
// replaceNode != null 说明删除的结点有一个孩子,直接变成黑色即可
while ((replaceNode == null || replaceNode.color == BLACK) && root != replaceNode) {
//删除结点是左孩子
if (replaceNode == parent.left) {
//兄弟结点为父结点的右孩子
Node brother = parent.right;
//Case1:(兄弟红-兄右) 将父亲结点变红,兄弟节点变黑,父结点左旋
if (brother != null && brother.color == RED) {
brother.color = BLACK;
parent.color = RED;
leftRotate(parent);
brother = parent.right; //因为后面要交换兄弟节点和父结点颜色
}
//Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
if (brother == null || (brother.left == null && brother.right == null)
|| (brother.left != null && brother.right != null && brother.left.color == BLACK && brother.right.color == BLACK)) {
if (brother != null) brother.color = RED;
replaceNode = parent;
parent = parent.parent;
continue;
}
//Case3:(兄黑-兄子不全黑-兄右子黑) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
if (brother.left != null && brother.left.color == RED) {
brother.color = RED;
brother.left.color = BLACK;
rightRotate(brother);
//更新兄弟结点
brother = brother.parent;
}
//Case4:(兄黑-兄子不全黑-兄右子红) 交换父结点和兄弟结点颜色, 父节点左旋,最后兄右孩子置黑,跳出循环
brother.color = parent.color;
parent.color = BLACK;
brother.right.color = BLACK;
leftRotate(parent);
replaceNode = root;
} else { //删除结点是右孩子或者为nil
//兄弟结点为父结点的左孩子
Node brother = parent.left;
//Case1:(兄弟红-兄左) 交换父节点和兄弟结点颜色(父变红,兄变黑),父结点右旋
if (brother != null && brother.color == RED) {
brother.color = BLACK;
parent.color = RED;
rightRotate(parent);
brother = parent.left;
}
//Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
if (brother == null || (brother.left == null && brother.right == null)
|| (brother.left != null && brother.right != null && brother.left.color == BLACK && brother.right.color == BLACK)) {
if (brother != null) brother.color = RED;
replaceNode = parent;
parent = parent.parent;
continue;
}
//Case3:(兄黑-兄子不全黑-兄左子黑(nil),右子红) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
if (brother.right != null && brother.right.color == RED) {
brother.color = BLACK;
brother.right.color = BLACK;
leftRotate(brother);
//更新兄弟结点
brother = brother.parent;
}
//Case4:(兄黑-兄子不全黑-兄左子红,右子黑(nil)) 交换父结点和兄弟结点颜色, 父节点右旋,最后兄左孩子置黑,跳出循环
brother.color = parent.color;
parent.color = BLACK;
brother.left.color = BLACK;
rightRotate(parent);
replaceNode = root;
}
}
//删除结点只有一个孩子,那么对替换结点改变颜色即可
if (replaceNode != null) replaceNode.color = BLACK;
}
/**
* 左旋
* 画图理解
*
* @param h 要旋转的目标结点
*/
private void leftRotate(Node h) {
Node son = h.right; //目标结点的右孩子
h.right = son.left; //设置旋转之后目标结点的右孩子
if (son.left != null) son.left.parent = h; //设置son.left的父节点
son.parent = h.parent; //设置son结点的父节点
if (h.parent == null) { //若原来目标结点父节点为null(根节点),将son设为根节点
root = son;
} else {
//走到这里,原来目标结点一定有父节点
//判断h是父节点的左孩子还是右孩子,并设置其孩子
if (h.parent.left == h) {
h.parent.left = son;
} else {
h.parent.right = son;
}
}
//设置son的左孩子
son.left = h;
//最后将原来目标结点的父节点设置为son
h.parent = son;
}
/**
* 右旋
* 画图理解
*
* @param h 要旋转的目标结点
*/
private void rightRotate(Node h) {
Node son = h.left; //目标结点的左孩子
h.left = son.right; //设置旋转之后目标结点的左孩子
if (son.right != null) son.right.parent = h; //设置son.right的父节点
son.parent = h.parent; //设置son结点的父节点
if (h.parent == null) { //若原来目标结点父节点为null(根节点),将son设为根节点
root = son;
} else {
//走到这里,原来目标结点一定有父节点
//判断h是父节点的左孩子还是右孩子,并设置其孩子
if (h.parent.left == h) {
h.parent.left = son;
} else {
h.parent.right = son;
}
}
//设置son的左孩子
son.right = h;
//最后将原来目标结点的父节点设置为son
h.parent = son;
}
/**
* 查找key值对应的结点,提供给外部的方法
*
* @param key
* @return
*/
public Node get(K key) {
return get(root, key);
}
/**
* 查找红黑树中键值为key的结点,内部使用的方法
* 递归实现
*
* @param node
* @param key
* @return
*/
private Node get(Node node, K key) {
if (node == null) return null;
int com = key.compareTo(node.key);
if (com < 0) {
return get(node.left, key);
} else if (com > 0) {
return get(node.right, key);
} else {
return node;
}
}
/**
* 中序遍历
*
* @param root root
*/
public void inOrder(Node root) {
if (root == null) return;
if (null != root.left) inOrder(root.left);
System.out.println(root);
if (null != root.right) inOrder(root.right);
}
@Data
class Node {
boolean color;
K key;
Node left;
Node right;
Node parent;
public Node(boolean color, K key, Node left, Node right, Node parent) {
this.color = color;
this.key = key;
this.left = left;
this.right = right;
this.parent = parent;
}
@Override
public String toString() {
String colorText = color ? "黑色" : "红色";
return "RBNode{" +
"color=" + colorText +
", key=" + key +
", left=" + (left == null ? "" : left.getKey()) +
", right=" + (right == null ? "" : right.getKey()) +
", parent=" + (parent == null ? "" : parent.getKey()) +
'}';
}
}
}