一、介绍
红黑树是一种特殊的二叉搜索平衡树,特点:
- 每个结点不是红色就是黑色(两种颜色的结点就可以了,为了研究方便这里使用了红黑色)
- 不可能有连在一起的结点
- 根结点都是黑色 root
- 每个红色结点的子结点都是黑色
- 叶子结点都是黑色 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
二、旋转方式
这里说明站在绿色结点的角度说的,但是左旋右旋操作是对黄色结点来说的,下面是黄色结点的左旋右旋
1、左旋
(1)以父结点为旋转点,当前结点是他的右子结点,当前结点逆时针旋转120度
(2)使得原先的父结点变成左子结点,当前结点,变成父结点
(3)同时祖父结点把指向原来父结点是指针指向当前结点
(4)此时当前结点的左子结点成为原来父结点的右子结点
如图:
2、右旋
(1)以父结点为旋转点,当前结点是他的左子结点,当前结点顺时针旋转120度
(2)使得原先的父结点变成右子结点,当前结点,变成父结点
(3)同时祖父结点把指向原来父结点是指针指向当前结点
(4)此时当前结点的右子结点成为原来父结点的左子结点
如图:
三、变换规则
所以的插入结点都是红色
1、变色
当前结点的父结点和叔结点都是红色:
(1)父结点和叔结点设置为黑色
(2)祖父结点设置为红色
(3)把指向当前的操作指针指向祖父结点(操作对象由当前结点变更成祖父结点,对祖父结点操作)
2、左旋
当前结点的父结点是红色,叔结点是黑色,且当前结点是右子树:
(1)以父结点左旋
(2)把指向当前的操作指针指向原来的父结点
3、右旋
当前结点的父结点是红色,叔结点是黑色,且当前结点是左子树:
(1)把父结点设置成黑色
(2)把祖父结点设置成红色
(3)以祖父结点右旋
(2)把指向当前的操作指针指向原来的祖父结点
4、例子
向一个红黑树中插入一条记录6,看红黑树调整过程,这里省去了NIL结点,菱形表示当前操作结点
如图:
到这里完成了红黑树的调整,再次变成红黑树了
四、代码实现
首先定义一下结点:
public class Node {
/**
* 保存的数据
*/
private int data;
/**
* 颜色
*/
private String color;
/**
* 左子结点
*/
private Node left;
/**
* 右子节点
*/
private Node right;
/**
* 父结点
*/
private Node parent;
/**
* 相对父结点的位置
*/
private String place;
/******************** 此处省略getter和setter ************************
}
创建红黑树对象
public class RedBlackTree {
// 结点颜色
public static final String CR = "red";
public static final String CB = "black";
// 相对于父结点的位置
public static final String L = "left";
public static final String R = "right";
/**
* 根节点
*/
private Node root;
/**
* 树的数据
*/
private Node tree;
/**
* 创建根结点
*
* @param date
*/
public RedBlackTree(int date) {
Node node = new Node();
node.setData(date);
node.setColor(CB);
this.root = node;
this.tree = node;
}
/**
* 获取当前树的根结点
*
* @return
*/
public RedBlackTree updateRoot() {
if (this.root.getParent() == null) {
return this;
} else {
this.root = this.root.getParent();
return updateRoot();
}
}
/**
* 添加结点
*
* @param args
* @return
*/
public Node addNode(int[] args) {
for (int i : args) {
add(tree, i);
// 更新当前的根结点
updateRoot();
}
return root;
}
/**
* 逐一添加结点
*
* @param tree
* @param date
* @return
*/
private Node add(Node tree, int date) {
if (tree.getData() < date) {
if (tree.getRight() == null) {
Node node = new Node();
node.setData(date);
node.setColor(CR);
node.setParent(tree);
node.setPlace(R);
tree.setRight(node);
/**
* 插入数据后可能会破坏红黑树结构
* 这里重新获取红黑树结构
*/
change(node);
} else {
add(tree.getRight(), date);
}
} else {
if (tree.getLeft() == null) {
Node node = new Node();
node.setData(date);
node.setColor(CR);
node.setParent(tree);
node.setPlace(L);
tree.setLeft(node);
/**
* 插入数据后可能会破坏红黑树结构
* 这里重新获取红黑树结构
*/
change(node);
} else {
add(tree.getLeft(), date);
}
}
return tree;
}
/**
* 输出当前结点的树
*
* @param tree
*/
public void print(Node tree) {
if (tree == null) {
return;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("data:").append(tree.getData()).append(",");
stringBuilder.append("color:").append(tree.getColor()).append(",");
if (tree.getParent() == null) {
stringBuilder.append("parent:").append("root").append(",");
} else {
stringBuilder.append("parent:").append(tree.getParent().getData()).append(",");
}
stringBuilder.append("place:").append(tree.getPlace()).append(",");
System.out.println(stringBuilder.toString());
print(tree.getLeft());
print(tree.getRight());
}
/**
* 根据变换规则实现变色操作
*
* @param tree 操作的结点
* @return 返回操作结点的祖父结点
*/
public Node changeColor(Node tree) {
tree.getParent().getParent().getLeft().setColor(CB);
tree.getParent().getParent().getRight().setColor(CB);
tree.getParent().getParent().setColor(CR);
return tree.getParent().getParent();
}
/**
* 实现左旋
*
* @param tree 操作的结点
* @return 然后操作的结点
*/
public Node spinLeft(Node tree) {
Node parent = tree.getParent();
Node right = tree.getRight();
Node rightLeft = tree.getRight().getLeft();
if (L.equals(tree.getPlace())) {
if (parent != null) {
parent.setLeft(right);
right.setParent(parent);
right.setPlace(L);
} else {
right.setParent(null);
right.setPlace(null);
}
right.setLeft(tree);
tree.setParent(right);
tree.setPlace(L);
tree.setRight(rightLeft);
rightLeft.setParent(tree);
rightLeft.setPlace(R);
} else {
if (parent != null) {
parent.setRight(right);
right.setParent(parent);
right.setPlace(R);
} else {
right.setParent(null);
right.setPlace(null);
}
right.setLeft(tree);
tree.setParent(right);
tree.setPlace(L);
tree.setRight(rightLeft);
rightLeft.setParent(tree);
rightLeft.setPlace(R);
}
return tree;
}
/**
* 实现右旋
*
* @param tree 操作的结点
* @return 然后操作的结点
*/
public Node spinRight(Node tree) {
Node parent = tree.getParent();
Node left = tree.getLeft();
Node leftRight = tree.getLeft().getRight();
if (L.equals(tree.getPlace())) {
if (parent != null) {
parent.setLeft(left);
left.setParent(parent);
left.setPlace(L);
} else {
left.setParent(null);
left.setPlace(null);
}
left.setRight(tree);
tree.setParent(left);
tree.setPlace(R);
tree.setLeft(leftRight);
leftRight.setParent(tree);
leftRight.setPlace(L);
} else {
if (parent != null) {
parent.setRight(left);
left.setParent(parent);
left.setPlace(R);
} else {
left.setParent(null);
left.setPlace(null);
}
left.setRight(tree);
tree.setParent(left);
tree.setPlace(R);
tree.setLeft(leftRight);
leftRight.setParent(tree);
leftRight.setPlace(L);
}
return tree;
}
/**
* 根据变化规则实现红黑树结果重组
*
* @param tree 操作的结点
* @return 操作的结点
*/
public Node change(Node tree) {
Node parent = tree.getParent();
// 如果没有父结点和祖父结点就不操作
if (parent == null || parent.getParent() == null) {
return tree;
}
// 获取叔结点
Node uncle = null;
if (L.equals(parent.getPlace())) {
uncle = parent.getParent().getRight();
} else {
uncle = parent.getParent().getLeft();
}
/**
* 当前结点的父结点和叔结点都是红色 变色
*/
Boolean flag1 = CR.equals(parent.getColor()) && CR.equals(uncle.getColor());
if (flag1) {
return change(changeColor(tree));
}
/**
* 当前结点的父结点是红色,叔结点是黑色,且当前结点是右子树
* 对父结点左旋
* 返回父结点
*/
Boolean flag2 = CR.equals(parent.getColor()) && CB.equals(uncle.getColor()) && R.equals(tree.getPlace());
if (flag2) {
return change(spinLeft(tree.getParent()));
}
/**
* 当前结点的父结点是红色,叔结点是黑色,且当前结点是左子树
* 把父结点设置成黑色
* 把祖父结点设置成红色
* 对祖父结点右旋
* 返回祖父结点
*/
Boolean flag3 = CR.equals(parent.getColor()) && CB.equals(uncle.getColor()) && L.equals(tree.getPlace());
if (flag3) {
tree.getParent().setColor(CB);
tree.getParent().getParent().setColor(CR);
return change(spinRight(tree.getParent().getParent()));
}
return tree;
}
public Node getRoot() {
return root;
}
}
测试函数
public class testTree {
public static void main(String[] args) {
// 创建根节点
RedBlackTree redBlackTree = new RedBlackTree(19);
// 插入数据
int[] datas = {5, 30, 1, 12, 7, 13, 35};
redBlackTree.addNode(datas);
//获取当前根结点,并输出完整的树
redBlackTree.print(redBlackTree.getRoot());
}
}
结果如下
data:19,color:red,parent:root,place:null,
data:5,color:red,parent:19,place:left,
data:1,color:black,parent:5,place:left,
data:12,color:black,parent:5,place:right,
data:7,color:red,parent:12,place:left,
data:13,color:red,parent:12,place:right,
data:30,color:black,parent:19,place:right,
data:35,color:red,parent:30,place:right,
通过上面是数据我们可以获得一棵树,这个树就是上面举例的树
现在再插入6;
public class testTree {
public static void main(String[] args) {
// 创建根节点
RedBlackTree redBlackTree = new RedBlackTree(19);
// 插入数据
int[] datas = {5, 30, 1, 12, 7, 13, 35};
redBlackTree.addNode(datas);
// 插入数据 6
int[] datas2 = {6};
redBlackTree.addNode(datas2);
//获取当前根结点,并输出完整的树
redBlackTree.print(redBlackTree.getRoot());
}
}
输出结果:
data:12,color:black,parent:root,place:null,
data:5,color:red,parent:12,place:left,
data:1,color:black,parent:5,place:left,
data:7,color:black,parent:5,place:right,
data:6,color:red,parent:7,place:left,
data:19,color:red,parent:12,place:right,
data:13,color:black,parent:19,place:left,
data:30,color:black,parent:19,place:right,
data:35,color:red,parent:30,place:right,
输出的正是我们要的最终结果,到这里就完成了上面例子的代码实现了。