红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。 红黑树的每个节点的属性除了有一个 key 和 3 个指针:parent、lchild、rchild 外 ,还有一个属性 :color:红或黑 。
与大多数二叉查找树一样 ,红黑树中较小的键值也是在左子树保存 。 红黑树除了具有二叉查找树所有性质外,还具有以下 5 点性质
①节点是红色或黑色。
②根是黑色。
③所有叶子都是黑色(叶子是 NUIL 节点)。
④每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
⑤从任一个节点到其每个叶子的所有路径都包含相同数目的黑色节点。
引用自《红黑树关键算法研究》–马国富,张涵
上图中6为待插入的节点,此时破坏了红黑树的第四个规则,出现了两个连续的红色节点
此时的情况为6的父节点(7)和叔叔节点都为红色,需要将爷爷(12)节点变为红色,叔叔节点(13)和父节点(7)变为黑色,如图
此时5和12 同为红色,不符合红黑树的定义,观察得叔叔(30)节点为爷爷节点的右节点,而12节点为父节点的右节点,此时需要进行对5进行左旋,如图
此时看到12和5都为红色,不满足红黑树的性质,此时以5节点的叔叔节点(30)为黑色,将父节点(12)变黑,将爷爷节点(19)变红,再对爷爷节点19进行右旋,如图
平衡了,出现了叔叔节点在左子树或右子树的情况,其实方法大致相同,可根据上述几种情况推出
package com.kangin.util;
/**
* 1.创建Rtree,定义颜色
* 2.创建RBNode
* 3.辅助方法定义:parentOf(node),isRed(node),setRed(node),setBlack(node),inOrderPrint()
* 4.左旋方法定义:leftRotate(node)
* 5.右旋方法定义:RightRotate(node)
* 6.公开插入接口方法定义:insert(K key, V value);
* 7.内部插入接口方法定义:insert(RBNode node);
* 8.修正插入导致红黑树失衡的方法定义:insertFIxUp(RBNode node);
* 9.测试红黑树正确性
*
* @author khb
* @date 2021/1/27
*/
public class MyRBtree<K extends Comparable<K>, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
private MyRBNode root; // 根节点
/**
* insert的开放方法
*
* @param key
* @param value
*/
public void insert(K key, V value) {
MyRBNode node = new MyRBNode();
node.setKey(key);
node.setValue(value);
node.setColor(RED);
insert(node);
}
/**
* 插入节点
*
* @param node
*/
private void insert(MyRBNode node) {
//查找要放位置的父节点
MyRBNode parent = null;
MyRBNode x = this.root;
while (x != null) {
parent = x;
int cmp = node.key.compareTo(x.key);
//cmp > 0 在右边查找
//cmp = 0 替换值
//cmp < 0 在左边查找
if (cmp > 0) {
x = x.right;
} else if (cmp == 0) {
x.setValue(node.getValue());
return;
} else {
x = x.left;
}
}
node.parent = parent;
int cmp = node.key.compareTo(parent.key);
if (cmp > 0) {
parent.right = node;
} else {
parent.left = node;
}
insertFIxUp(node);
}
/**
* 平衡红黑树的方法
* 1.为根节点
* 2.相等
* 3.父亲为黑色
*
* @param node
* @return
*/
private void insertFIxUp(MyRBNode node) {
this.root.color = BLACK;
MyRBNode parent = parentOf(node);
MyRBNode gparent = parentOf(parent);
//-------------------------------parent为空代表根节点,不予处理,若父节点为黑色,也不予处理----------------------------------------------------------------/
if (parent != null && isRed(parent)) {
MyRBNode uncle = null;
if (parent == gparent.left) {
//-------------------------------当叔叔节点是右子树时,即父节点处在左子树上----------------------------------------------------------------/
uncle = gparent.right;
//------------1.如果父亲是红,叔叔也是红 将父亲叔叔变成黑色,将爷爷变成红色,用爷爷节点进入下一个循环--------------------------------------------/
if (uncle != null && isRed(uncle)) {
setBlack(parent);
setBlack(uncle);
setRed(gparent);
insertFIxUp(gparent);
return;
}
if (uncle == null || isBlack(uncle)) {
//如果叔叔为黑色或者不存在时,判断在左子树还是右子树
if (node == parent.left) {
//如果是左子树,将他的父节点变黑,爷爷节点变红,以爷爷节点进行右旋
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
return;
} else {
leftRotate(parent);
insertFIxUp(parent);
return;
}
}
} else {
//-------------------------------当叔叔节点是左子树时,即父节点处在右子树上----------------------------------------------------------------/
uncle = gparent.left;
//如果父亲是红,叔叔也是红 将父亲叔叔变成黑色,将爷爷变成红色,用爷爷节点进入下一个循环
if (uncle != null && isRed(uncle)) {
setBlack(parent);
setBlack(uncle);
setRed(gparent);
insertFIxUp(gparent);
return;
}
if (uncle == null || isBlack(uncle)) {
if (node == parent.right) {
//如果是右子树,将他的父节点变黑,爷爷节点变红,以爷爷节点进行左旋
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
return;
}
if (node == parent.left) {
rightRotate(parent);
insertFIxUp(parent);
return;
}
}
}
}
}
//获取父节点
private MyRBNode parentOf(MyRBNode node) {
if (node != null) {
return node.parent;
}
return null;
}
/**
* 判断该节点是否为红节点
*
* @param node
* @return
*/
private boolean isRed(MyRBNode node) {
if (node != null) {
return node.color == RED;
}
return false;
}
/**
* 判断该节点是否为黑节点
*
* @param node
* @return
*/
private boolean isBlack(MyRBNode node) {
if (node != null) {
return node.color == BLACK;
}
return false;
}
/**
* 置为红色,置为黑色
*
* @param node
*/
private void setRed(MyRBNode node) {
if (node != null) {
node.color = RED;
}
}
private void setBlack(MyRBNode node) {
if (node != null) {
node.color = BLACK;
}
}
/**
* 左旋 : p p
* | |
* x y
* / \ ----> / \
* lx y x ry
* / \ / \
* ly ry lx ly
*
* 1.将x的右节点指向y的左节点,将y的左节点的父节点指向x
* 2. 当p不为空时,将y的父节点指向x的父节点,判断左右子树将此时y的父节点左或者右指向y,
* 当p为空时,更新root节点,并将y的parent置空
* 3.将x的父节点指向y,将y的左子树指向x
*/
private void leftRotate(MyRBNode x) {
MyRBNode y = x.right;
//1.将x的右节点指向y的左节点,将y的左节点的父节点指向x
x.right = y.left;
if (y.left != null) {
y.left.parent = x;
}
//2. 当p不为空时,将y的父节点指向x的父节点,判断左右子树将此时y的父节点左或者右指向y,
// 当p为空时,更新root节点,并将y的parent置空
if (x.parent != null) {
y.parent = x.parent;
if (x == x.parent.left) {
x.parent.left = y;
} else {
x.parent.right = y;
}
} else {
this.root = y;
this.root.parent = null;
}
//3.将x的父节点指向y,将y的左子树指向x
x.parent = y;
y.left = x;
}
/**
* 右旋方法
* 右旋示意图
* p p
* | |
* y x
* / \ ------> / \
* x ry lx y
* / \ / \
* lx rx rx ry
*
* 1.将y的左子树指向x的右子树,将x的右子树的父节点指向y
* 2.当p不为空时,将x的父节点指向y的父节点,此处判断x为其父节点的左右节点,再将x的父节点的左右节点指向y
* 当p为空时。根节点就为x
* 3.将y的父节点指向x,将x的右子树指向y
*/
private void rightRotate(MyRBNode y) {
MyRBNode x = y.left;
//1.将y的左子树指向x的右子树,将x的右子树的父节点指向y
y.left = x.right;
if (x.right != null) {
x.right.parent = y;
}
//2.当p不为空时,将x的父节点指向y的父节点,此处判断x为其父节点的左右节点,再将x的父节点的左右节点指向y
if (y.parent != null) {
x.parent = y.parent;
if (y == y.parent.left) {
y.parent.left = x;
} else {
y.parent.right = x;
}
} else {
// 当p为空时。根节点就为x
this.root = x;
this.root.parent = null;
}
//3.将y的父节点指向x,将x的右子树指向y
y.parent = x;
x.right = y;
}
//节点内部类
static class MyRBNode<K extends Comparable<K>, V> {
private MyRBNode parent;
private MyRBNode left;
private MyRBNode right;
private boolean color;
private K key;
private V value;
public MyRBNode() {
}
public MyRBNode(MyRBNode parent, MyRBNode left, MyRBNode right, boolean color, K key, V value) {
this.parent = parent;
this.left = left;
this.right = right;
this.color = color;
this.key = key;
this.value = value;
}
public MyRBNode getParent() {
return parent;
}
public void setParent(MyRBNode parent) {
this.parent = parent;
}
public MyRBNode getLeft() {
return left;
}
public void setLeft(MyRBNode left) {
this.left = left;
}
public MyRBNode getRight() {
return right;
}
public void setRight(MyRBNode right) {
this.right = right;
}
public boolean isColor() {
return color;
}
public void setColor(boolean color) {
this.color = color;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
}