红黑树的Java代码实现
目录
- 红黑树的介绍
- 红黑树的Java实现(代码说明)
- 1.基本定义
- 2.左旋
- 3.右旋
- 4.添加
- 5.添加修正
- 6.删除
- 7.删除修正
- 红黑树的Java实现(完整源码)
- 总结
红黑树的介绍
R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。
红黑树的特性(规则):
(1)每个节点或者是黑色(Black),或者是红色(Red)。
(2)根节点是黑色(Black)。
(3)每个叶子节点(NIL
)是黑色。 [注意:这里叶子节点,是指为空(NIL
或NULL
)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
注意:
(01) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
(02) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
因此,满足以上五条特性的二叉树就叫做红黑树。
红黑树示意图如下:
建议根据以上红黑树的五条特性查看:
红黑树的Java实现(代码说明)
1.基本定义
红黑树的基本操作是添加、删除和旋转。在对红黑树进行添加或删除后,会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。下面分别对红黑树的基本操作进行介绍。
红黑树的基本类的实现代码(Java语言)
/* 用内部类的方式实现Node类 */
public class RBTree<T> implements Tree<T> {
private RBTNode<T> root;//根节点
private static final boolean BLACK = true;
private static final boolean RED = false;
private class RBTNode<T>{
T key; // 关键字(键值)
boolean color; //颜色
RBTNode<T> parent; //父亲节点
RBTNode<T> lchild;//左孩子
RBTNode<T> rchild;//右孩子
public RBTNode() {
// TODO Auto-generated constructor stub
}
public RBTNode(T key,boolean color,RBTNode<T> parent,RBTNode<T> lchild,RBTNode<T> rchild) {
this.key = key;
this.color = color;
this.parent = parent;
this.lchild = lchild;
this.rchild = rchild;
}
}
......
}
注意:RBTree是红黑树对应的类,RBTNode是红黑树的节点类,Tree是红黑树对应的API接口,在RBTree中包含了根节点mRoot和红黑树的相关API。
2.左旋
左旋的实现代码(Java语言)
//左旋
private void leftRotate(RBTNode<T> x) {
//当前是针对x节点进行左旋
//设y为x的右孩子,经过左旋y就会到x当前的位置
RBTNode<T> y = x.rchild;
//将y的左孩子设为x的右孩子
x.rchild = y.lchild;
//如果y的左孩子非空的话就设置它的父节点,
//不然就是空节点,空节点无必要设置父节点
if(y.lchild != null) {
//将x设为y的左孩子的父节点
y.lchild.parent = x;
}
//将x的父节点设为y的父节点
y.parent = x.parent;
if(x.parent != null) { //如果x的父节点非空
if(x.parent.lchild == x) {
x.parent.lchild = y; //如果x为它父节点的左孩子,则将y设为x父节点的左孩子
}else {
x.parent.rchild = y; //如果x为它父节点的右孩子,则将y设为x父节点的右孩子
}
}else {
this.root = y; //如果x父节点为空,则将y设为根节点
}
y.lchild = x; //将x设为y的左孩子
x.parent = y; //最后再将y设为x的父节点,左旋到此结束!
}
3.右旋
右旋的实现代码(Java语言)
//右旋
private void rightRotate(RBTNode<T> x) {
//当前是针对x节点进行右旋
//设y为x的左孩子,经过右旋y就会到x当前的位置
RBTNode<T> y = x.lchild;
//将y的右孩子设为x的左孩子
x.lchild = y.rchild;
//如果y的右孩子非空的话就设置它的父节点,
//不然就是空节点,空节点无必要设置父节点
if(y.rchild != null) {
//将x设为y的右孩子的父节点
y.rchild.parent = x;
}
//将x的父节点设为y的父节点
y.parent = x.parent;
if(x.parent != null) { //如果x的父节点非空
if(x.parent.lchild == x) {
x.parent.lchild = y; //如果x为它父节点的左孩子,则将y设为x父节点的左孩子
}else {
x.parent.rchild = y; //如果x为它父节点的右孩子,则将y设为x父节点的右孩子
}
}else {
this.root = y; //如果x父节点为空,则将y设为根节点
}
y.rchild = x; //将x设为y的左孩子
x.parent = y; //最后再将y设为x的父节点,左旋到此结束!
}
4.添加
将一个节点插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过"旋转和重新着色"等一系列操作来修正该树,使之重新成为一颗红黑树。详细描述如下:
第一步: 将红黑树当作一颗二叉查找树,将节点插入。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
好吧?那接下来,我们就来想方设法的旋转以及重新着色,使这颗树重新成为红黑树!
第二步:将插入的节点着色为"红色"。
为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。
第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
对于"特性(4)“,是有可能违背的!
那接下来,想办法使之"满足特性(4)”,就可以将树重新构造成红黑树了。
添加操作的实现代码(Java语言)
//插入节点
private void insert(RBTNode<T> node) {
RBTNode<T> mroot = this.root;
RBTNode<T> nodeparent = null;
//以二叉查找树的方式插入节点就行
while(mroot!=null) {
nodeparent = mroot;
if((int)node.key<(int)mroot.key) {
mroot = mroot.lchild;
}else {
mroot = mroot.rchild;
}
}
//设置插入节点的父亲节点
node.parent = nodeparent;
if(nodeparent != null) {
if((int)node.key<(int)nodeparent.key) {
nodeparent.lchild = node;
}else {
nodeparent.rchild = node;
}
}else {
this.root = node;
}
//每次插入节点后进行设置颜色默认为红色,然后进插入修正使之成为红黑树
setRed(node);
//进入插入修正算法,是红黑树中的核心算法之一
insertFixUp(node);
}
//公开的插入方法
public void insert(T key) {
RBTNode<T> node = new RBTNode<T>(key, this.BLACK, null, null, null);
//如果新建节点非null进入插入节点方法,不然返回
if(node != null) {
insert(node);
}
}
注意:
内部接口 – insert(node)的作用是将"node"节点插入到红黑树中。
外部接口 – insert(key)的作用是将"key"添加到红黑树中。
5.添加修正
因为时间问题,这里就不再详细说了
好,言归正传。
添加修正操作的实现代码(Java语言)
//插入修正算法,红黑树的核心算法之一
private void insertFixUp(RBTNode<T> node) {
//插入节点node的父亲节点,祖父节点和叔叔节点
RBTNode<T> parent,gparent,uncle;
//父亲节点非空 并且 父亲节点为红色才会进入
while(((parent=parentOf(node)) != null) && isRed(parent)) {
gparent = parentOf(parent);
if(gparent.lchild == parent) {
uncle = gparent.rchild;
//Case 1条件: 叔叔节点是红色节点
if(uncle!=null && isRed(uncle)) {
//操作
setBlack(parent);
setBlack(uncle);
setRed(gparent);
node = gparent;
continue;
}else {
//Case 2条件: 叔叔节点是黑色节点,且当前节点是父亲节点的右孩子
if(parent.rchild == node) {
//操作
leftRotate(parent);
RBTNode<T> tmp = parent;
parent = node;
node = tmp;
}
//Case 3条件: 叔叔节点是黑色节点,且当前节点是父亲节点的左孩子
//操作
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
}
}else { //同理(左右相反)
uncle = gparent.lchild;
//Case 1条件: 叔叔节点是红色
if(uncle!=null && isRed(uncle)) {
//操作
setBlack(parent);
setBlack(uncle);
setRed(gparent);
node = gparent;
continue;
}else {
//Case 2条件: 叔叔节点是黑色,且当前节点是父亲节点的左孩子
if(parent.lchild == node) {
//操作
rightRotate(parent);
RBTNode<T> tmp = parent;
parent = node;
node = tmp;
}
//Case 3条件: 叔叔节点是黑色,且当前节点是父亲节点的右孩子
//操作
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
}
}
}
//如果插入节点为根节点设为黑色,根节点总是黑色的
if(node == this.root) {
setBlack(node);
}
}
insertFixUp(node)的作用是对应"上面所讲的第三步"。它是一个内部接口。
6.删除
将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。详细描述如下:
第一步:将红黑树当作一颗二叉查找树,将节点删除。
这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。
第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
删除操作的实现代码(Java语言)
//删除节点
private void remove(RBTNode<T> node) {
//存放替换节点的孩子节点,父亲节点和替换节点
RBTNode<T> child,parent,replace;
//保存颜色用来判断是否需要进入删除修正
boolean color = this.BLACK;
//删除节点一共分为两种情况
//待删除节点的左右孩子非空的情况下
if((node.lchild != null) && (node.rchild != null)) {
//获取替换节点
replace = successor(node);
//因为替换节点是最小后继节点,所以它要不没有节点,要不只有右孩子节点
child = replace.rchild;
//获取替换节点的父亲节点
parent = parentOf(replace);
//获取真正要删除的节点颜色
color = colorOf(replace);
if(node == parentOf(replace)) {
//直接将替换节点设给待删除结点
parent = replace;
}else {
//将被删除节点的代替的后驱节点的孩子节点的父节点设成后驱节点的父亲
if(child != null) {
child.parent = replace.parent;
}
//以下为后驱节点代替被删除节点的操作以及步骤
replace.parent.lchild = child;
setParent(node.rchild, replace);
replace.rchild = node.rchild;
}
setParent(replace, parentOf(node));
setParent(node.lchild, replace);
replace.lchild = node.lchild;
//到此被删除节点删除完成
setColor(replace, colorOf(node));
if(node.parent == null) {
this.root = replace;
}else {
if(node.parent.lchild == node) {
node.parent.lchild = replace;
}else {
node.parent.rchild = replace;
}
}
//到此被删除节点不仅删除完成,而且后驱节点代替完成
//进入删除修正
if(color == this.BLACK) {
removeFixUp(child, parent);
}
}else { //如果被删除节点只有一个孩子或者没有孩子的情况下进入
//这里比较好理解,不再详细解释
if(node.lchild != null) {
replace = node.lchild;
}else {
replace = node.rchild;
}
parent = parentOf(node);
color = colorOf(node);
child = replace;
setParent(replace, parentOf(node));
if(node.parent == null) {
this.root = replace;
}else {
if(node.parent.lchild == node) {
node.parent.lchild = replace;
}else {
node.parent.rchild = replace;
}
}
//进入删除修正
if(color == this.BLACK) {
removeFixUp(child, parent);
}
}
}
//删除查找到的数据
public void remove(T key) {
RBTNode<T> node;
if((node = search(key, this.root)) != null) {
System.out.println("需要删除的节点;"+node.key);
remove(node);
}
}
内部接口 – remove(node)的作用是将"node"节点插入到红黑树中。
外部接口 – remove(key)删除红黑树中键值为key的节点。
7.删除修正
因为时间问题,所以跟前面一样,这里就不再详细说了
最后一哆嗦了,不在废话,直接安排代码!!!go,go,go
删除修正操作的实现代码(Java语言)
//删除修正算法,红黑树的核心算法之一
private void removeFixUp(RBTNode<T> node,RBTNode<T> parent) {
//被删除节点的兄弟节点
RBTNode<T> other;
while((node==null || isBlack(node)) && (this.root != node)) {
if(parent.lchild == node) {
//获取兄弟节点
other = parent.rchild;
//Case 1: 兄弟节点是红色
if(other != null && isRed(other)) {
//操作
setBlack(other);
setRed(parent);
leftRotate(parent);
other = parent.rchild;
continue;
}else {
//Case 2: 兄弟节点是黑色,且兄弟节点的左右孩子节点为黑色
if((other.lchild==null || isBlack(other.lchild)) &&
(other.rchild==null || isBlack(other.rchild)) ) {
//操作
setRed(other);
node = parent;
parent = parentOf(node);
}else {
//Case 3: 兄弟节点是黑色,且兄弟节点的左孩子节点为红色。右孩子节点为黑色
if(isRed(other.lchild) && (other.rchild==null || isBlack(other.rchild))) {
//操作
setBlack(other.lchild);
setRed(other);
rightRotate(other);
other = parent.rchild;
}
//Case 4: 兄弟节点是黑色,且兄弟节点的右孩子节点为红色。左孩子节点任意色
//操作
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.rchild);
leftRotate(parent);
node = this.root;
break;
}
}
}else { //同理(左右相反)
//获取兄弟节点
other = parent.lchild;
//Case 1: 兄弟节点是红色
if(other != null && isRed(other)) {
//操作
setBlack(other);
setRed(parent);
rightRotate(parent);
other = parent.lchild;
continue;
}else {
//Case 2: 兄弟节点是黑色,且兄弟节点的左右孩子节点为黑色
if((other.lchild==null || isBlack(other.lchild)) &&
(other.rchild==null || isBlack(other.rchild)) ) {
//操作
setRed(other);
node = parent;
parent = parentOf(node);
}else {
//Case 3: 兄弟节点是黑色,且兄弟节点的右孩子节点为红色。左孩子节点为黑色
if(isRed(other.rchild) && (other.lchild==null || isBlack(other.lchild))) {
//操作
setBlack(other.rchild);
setRed(other);
leftRotate(other);
other = parent.lchild;
}
//Case 4: 兄弟节点是黑色,且兄弟节点的左孩子节点为红色。右孩子节点任意色
//操作
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.lchild);
rightRotate(parent);
node = this.root;
break;
}
}
}
}
//跳出循坏后node为根节点,因此为了以防万一设置为黑色
if(node != null) {
setBlack(node);
}
}
removeFixup(node, parent)是对应"上面所讲的第三步"。它是一个内部接口。
红黑树的Java实现(完整源码)
如果之前有不理解的或者其他,你都可以不管,我咔咔手撕红黑树源码整给你们,直接复制就能跑!!!看我这么为大家着想的份上,关注一下呗!!!
红黑树完整源代码(Java实现)
public class RBTree<T> implements Tree<T> {
private RBTNode<T> root;//根节点
private static final boolean BLACK = true;
private static final boolean RED = false;
private class RBTNode<T>{
T key; // 关键字(键值)
boolean color; //颜色
RBTNode<T> parent; //父亲节点
RBTNode<T> lchild;//左孩子
RBTNode<T> rchild;//右孩子
public RBTNode() {
// TODO Auto-generated constructor stub
}
public RBTNode(T key,boolean color,RBTNode<T> parent,RBTNode<T> lchild,RBTNode<T> rchild) {
this.key = key;
this.color = color;
this.parent = parent;
this.lchild = lchild;
this.rchild = rchild;
}
}
//获取父亲节点
public RBTNode<T> parentOf(RBTNode<T> node){
if(node != null) {
return node.parent;
}
return null;
}
//获取颜色
public boolean colorOf(RBTNode<T> node) {
if(node != null) {
return node.color;
}
return this.BLACK;
}
//设置父亲节点
public void setParent(RBTNode<T> node,RBTNode<T> parent) {
if(node != null) {
node.parent = parent;
}
}
//设置颜色
public void setColor(RBTNode<T> node,boolean color) {
if(node != null) {
node.color = color;
}
}
//判断是否红色
public boolean isRed(RBTNode<T> node) {
return (node != null && node.color==this.RED)?true:false;
}
//判断是否黑色
public boolean isBlack(RBTNode<T> node) {
return !isRed(node);
}
//设置为红色
public void setRed(RBTNode<T> node) {
if(node != null) {
node.color = this.RED;
}
}
//设置为黑色
public void setBlack(RBTNode<T> node) {
if(node != null) {
node.color = this.BLACK;
}
}
//搜索当前要删除节点是否存在
public RBTNode<T> search(T key,RBTNode<T> node){
while(node != null) {
if((int)key<(int)node.key) {
node = node.lchild;
}else if((int)key>(int)node.key) {
node = node.rchild;
}else {
return node;
}
}
return null;
}
//找到要删除节点的后继最小节点
public RBTNode<T> min(RBTNode<T> node){
if(node.lchild == null) {
return node;
}
while(node.lchild != null) {
node = node.lchild;
}
return node;
}
//找到删除节点地后继节点
public RBTNode<T> successor(RBTNode<T> node){
if(node.rchild != null) {
return min(node.rchild);
}
RBTNode<T> y = node.parent;
while(y!=null && y.rchild == node) {
node = y;
y = y.parent;
}
return y;
}
//左旋
private void leftRotate(RBTNode<T> x) {
//当前是针对x节点进行左旋
//设y为x的右孩子,经过左旋y就会到x当前的位置
RBTNode<T> y = x.rchild;
//将y的左孩子设为x的右孩子
x.rchild = y.lchild;
//如果y的左孩子非空的话就设置它的父节点,
//不然就是空节点,空节点无必要设置父节点
if(y.lchild != null) {
//将x设为y的左孩子的父节点
y.lchild.parent = x;
}
//将x的父节点设为y的父节点
y.parent = x.parent;
if(x.parent != null) { //如果x的父节点非空
if(x.parent.lchild == x) {
x.parent.lchild = y; //如果x为它父节点的左孩子,则将y设为x父节点的左孩子
}else {
x.parent.rchild = y; //如果x为它父节点的右孩子,则将y设为x父节点的右孩子
}
}else {
this.root = y; //如果x父节点为空,则将y设为根节点
}
y.lchild = x; //将x设为y的左孩子
x.parent = y; //最后再将y设为x的父节点,左旋到此结束!
}
//右旋
private void rightRotate(RBTNode<T> x) {
//当前是针对x节点进行右旋
//设y为x的左孩子,经过右旋y就会到x当前的位置
RBTNode<T> y = x.lchild;
//将y的右孩子设为x的左孩子
x.lchild = y.rchild;
//如果y的右孩子非空的话就设置它的父节点,
//不然就是空节点,空节点无必要设置父节点
if(y.rchild != null) {
//将x设为y的右孩子的父节点
y.rchild.parent = x;
}
//将x的父节点设为y的父节点
y.parent = x.parent;
if(x.parent != null) { //如果x的父节点非空
if(x.parent.lchild == x) {
x.parent.lchild = y; //如果x为它父节点的左孩子,则将y设为x父节点的左孩子
}else {
x.parent.rchild = y; //如果x为它父节点的右孩子,则将y设为x父节点的右孩子
}
}else {
this.root = y; //如果x父节点为空,则将y设为根节点
}
y.rchild = x; //将x设为y的左孩子
x.parent = y; //最后再将y设为x的父节点,左旋到此结束!
}
//插入修正算法,红黑树的核心算法之一
private void insertFixUp(RBTNode<T> node) {
//插入节点node的父亲节点,祖父节点和叔叔节点
RBTNode<T> parent,gparent,uncle;
//父亲节点非空 并且 父亲节点为红色才会进入
while(((parent=parentOf(node)) != null) && isRed(parent)) {
gparent = parentOf(parent);
if(gparent.lchild == parent) {
uncle = gparent.rchild;
//Case 1条件: 叔叔节点是红色节点
if(uncle!=null && isRed(uncle)) {
//操作
setBlack(parent);
setBlack(uncle);
setRed(gparent);
node = gparent;
continue;
}else {
//Case 2条件: 叔叔节点是黑色节点,且当前节点是父亲节点的右孩子
if(parent.rchild == node) {
//操作
leftRotate(parent);
RBTNode<T> tmp = parent;
parent = node;
node = tmp;
}
//Case 3条件: 叔叔节点是黑色节点,且当前节点是父亲节点的左孩子
//操作
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
}
}else { //同理(左右相反)
uncle = gparent.lchild;
//Case 1条件: 叔叔节点是红色
if(uncle!=null && isRed(uncle)) {
//操作
setBlack(parent);
setBlack(uncle);
setRed(gparent);
node = gparent;
continue;
}else {
//Case 2条件: 叔叔节点是黑色,且当前节点是父亲节点的左孩子
if(parent.lchild == node) {
//操作
rightRotate(parent);
RBTNode<T> tmp = parent;
parent = node;
node = tmp;
}
//Case 3条件: 叔叔节点是黑色,且当前节点是父亲节点的右孩子
//操作
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
}
}
}
//如果插入节点为根节点设为黑色,根节点总是黑色的
if(node == this.root) {
setBlack(node);
}
}
//插入节点
private void insert(RBTNode<T> node) {
RBTNode<T> mroot = this.root;
RBTNode<T> nodeparent = null;
//以二叉查找树的方式插入节点就行
while(mroot!=null) {
nodeparent = mroot;
if((int)node.key<(int)mroot.key) {
mroot = mroot.lchild;
}else {
mroot = mroot.rchild;
}
}
//设置插入节点的父亲节点
node.parent = nodeparent;
if(nodeparent != null) {
if((int)node.key<(int)nodeparent.key) {
nodeparent.lchild = node;
}else {
nodeparent.rchild = node;
}
}else {
this.root = node;
}
//每次插入节点后进行设置颜色默认为红色,然后进插入修正使之成为红黑树
setRed(node);
//进入插入修正算法,是红黑树中的核心算法之一
insertFixUp(node);
}
//公开的插入方法
public void insert(T key) {
RBTNode<T> node = new RBTNode<T>(key, this.BLACK, null, null, null);
//如果新建节点非null进入插入节点方法,不然返回
if(node != null) {
insert(node);
}
}
//公开的插入方法
public void add(T key) {
RBTNode<T> node = new RBTNode<T>(key, this.BLACK, null, null, null);
if(node != null) {
insert(node);
}
}
//删除修正算法,红黑树的核心算法之一
private void removeFixUp(RBTNode<T> node,RBTNode<T> parent) {
//被删除节点的兄弟节点
RBTNode<T> other;
while((node==null || isBlack(node)) && (this.root != node)) {
if(parent.lchild == node) {
System.out.println("当前节点为父亲节点的左孩子节点");
//获取兄弟节点
other = parent.rchild;
System.out.println("当前父亲节点parent:"+parent.key+" other(兄弟节点):"+other.key);
//Case 1: 兄弟节点是红色
if(other != null && isRed(other)) {
System.out.println("Case 1: 兄弟节点是红色");
//操作
setBlack(other);
setRed(parent);
leftRotate(parent);
other = parent.rchild;
continue;
}else {
//Case 2: 兄弟节点是黑色,且兄弟节点的左右孩子节点为黑色
if((other.lchild==null || isBlack(other.lchild)) &&
(other.rchild==null || isBlack(other.rchild)) ) {
System.out.println("Case 2: 兄弟节点是黑色,且兄弟节点的左右孩子节点为黑色");
//操作
setRed(other);
node = parent;
parent = parentOf(node);
}else {
//Case 3: 兄弟节点是黑色,且兄弟节点的左孩子节点为红色。右孩子节点为黑色
if(isRed(other.lchild) && (other.rchild==null || isBlack(other.rchild))) {
System.out.println("Case 3: 兄弟节点是黑色,且兄弟节点的左孩子节点为红色。右孩子节点为黑色");
//操作
setBlack(other.lchild);
setRed(other);
rightRotate(other);
other = parent.rchild;
}
//Case 4: 兄弟节点是黑色,且兄弟节点的右孩子节点为红色。左孩子节点任意色
System.out.println("Case 4: 兄弟节点是黑色,且兄弟节点的右孩子节点为红色。左孩子节点任意色");
//操作
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.rchild);
leftRotate(parent);
node = this.root;
break;
}
}
}else { //同理(左右相反)
System.out.println("当前节点为父亲节点的右孩子节点");
//获取兄弟节点
other = parent.lchild;
System.out.println("当前父亲节点parent:"+parent.key+" other(兄弟节点):"+other.key);
//Case 1: 兄弟节点是红色
if(other != null && isRed(other)) {
System.out.println("Case 1: 兄弟节点是红色");
//操作
setBlack(other);
setRed(parent);
rightRotate(parent);
other = parent.lchild;
continue;
}else {
//Case 2: 兄弟节点是黑色,且兄弟节点的左右孩子节点为黑色
if((other.lchild==null || isBlack(other.lchild)) &&
(other.rchild==null || isBlack(other.rchild)) ) {
System.out.println("Case 2: 兄弟节点是黑色,且兄弟节点的左右孩子节点为黑色");
//操作
setRed(other);
node = parent;
parent = parentOf(node);
}else {
//Case 3: 兄弟节点是黑色,且兄弟节点的右孩子节点为红色。左孩子节点为黑色
if(isRed(other.rchild) && (other.lchild==null || isBlack(other.lchild))) {
System.out.println("Case 3: 兄弟节点是黑色,且兄弟节点的右孩子节点为红色。左孩子节点为黑色");
//操作
setBlack(other.rchild);
setRed(other);
leftRotate(other);
other = parent.lchild;
}
//Case 4: 兄弟节点是黑色,且兄弟节点的左孩子节点为红色。右孩子节点任意色
System.out.println("Case 4: 兄弟节点是黑色,且兄弟节点的左孩子节点为红色。右孩子节点任意色");
//操作
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.lchild);
rightRotate(parent);
node = this.root;
break;
}
}
}
}
if(node != null) {
setBlack(node);
System.out.println("当前node节点"+node.key+"染黑");
}
System.out.println("删除修正,红黑树调整完成!!!");
}
//删除节点
private void remove(RBTNode<T> node) {
//存放替换节点的孩子节点,父亲节点和替换节点
RBTNode<T> child,parent,replace;
//保存颜色用来判断是否需要进入删除修正
boolean color = this.BLACK;
//删除节点一共分为两种情况
//待删除节点的左右孩子非空的情况下
if((node.lchild != null) && (node.rchild != null)) {
//获取替换节点
replace = successor(node);
//因为替换节点是最小后继节点,所以它要不没有节点,要不只有右孩子节点
child = replace.rchild;
//获取替换节点的父亲节点
parent = parentOf(replace);
//获取真正要删除的节点颜色
color = colorOf(replace);
if(node == parentOf(replace)) {
//直接将替换节点设给待删除结点
parent = replace;
}else {
//将被删除节点的代替的后驱节点的孩子节点的父节点设成后驱节点的父亲
if(child != null) {
child.parent = replace.parent;
}
//以下为后驱节点代替被删除节点的操作以及步骤
replace.parent.lchild = child;
setParent(node.rchild, replace);
replace.rchild = node.rchild;
}
setParent(replace, parentOf(node));
setParent(node.lchild, replace);
replace.lchild = node.lchild;
//到此被删除节点删除完成
setColor(replace, colorOf(node));
if(node.parent == null) {
this.root = replace;
}else {
if(node.parent.lchild == node) {
node.parent.lchild = replace;
}else {
node.parent.rchild = replace;
}
}
//到此被删除节点不仅删除完成,而且后驱节点代替完成
//进入删除修正
if(color == this.BLACK) {
removeFixUp(child, parent);
}
}else { //如果被删除节点只有一个孩子或者没有孩子的情况下进入
//这里比较好理解,不再详细解释
if(node.lchild != null) {
replace = node.lchild;
}else {
replace = node.rchild;
}
parent = parentOf(node);
color = colorOf(node);
child = replace;
setParent(replace, parentOf(node));
if(node.parent == null) {
this.root = replace;
}else {
if(node.parent.lchild == node) {
node.parent.lchild = replace;
}else {
node.parent.rchild = replace;
}
}
//进入删除修正
if(color == this.BLACK) {
removeFixUp(child, parent);
}
}
}
//删除查找到的数据
public void remove(T key) {
RBTNode<T> node;
if((node = search(key, this.root)) != null) {
System.out.println("需要删除的节点;"+node.key);
remove(node);
}
}
//删除查找到的数据
public void delete(T key) {
RBTNode<T> node;
if((node = search(key, this.root)) != null) {
System.out.println("需要删除的节点;"+node.key);
remove(node);
}
}
//获取红黑树的高度
public int get_height(RBTNode<T> node) {
if(node == null)return -1;
int deep = 0,count = 0,nextcount = 1;
Queue<RBTNode<T>> queue = new LinkedList<RBTNode<T>>();
queue.add(node);
while(!queue.isEmpty()) {
RBTNode<T> head = queue.poll();
count++;
if(head.lchild != null)queue.add(node.lchild);
if(head.rchild != null)queue.add(head.rchild);
if(count == nextcount) {
count = 0;
nextcount = queue.size();
deep++;
}
}
return deep;
}
//获取左子树高度
public int get_leftHeight() {
return get_height(this.root.lchild);
}
//获取右子树高度
public int get_rightHeight() {
return get_height(this.root.rchild);
}
//先序遍历
public void preorder_traversal() {
if(this.root == null)return;
RBTNode<T> head = this.root;
String color;
Stack<RBTNode<T>> stack = new Stack<RBTNode<T>>();
stack.push(head);
while(!stack.isEmpty()) {
RBTNode<T> node = stack.pop();
if(isBlack(node)) {
color = "BLACK";
}else {
color = "RED";
}
if(node.parent == null) {
System.out.println("根节点:"+node.key+" : "+color+" 父亲节点:"+node.parent);
}else {
System.out.println(node.key+" : "+color+" 父亲节点:"+node.parent.key);
}
if(node.rchild != null)stack.push(node.rchild);
if(node.lchild != null)stack.push(node.lchild);
}
}
//中序遍历
public void inorder_traversal() {
if(this.root == null)return;
RBTNode<T> cur = this.root;
String color;
Stack<RBTNode<T>> stack = new Stack<RBTNode<T>>();
while(!stack.isEmpty() || cur != null) {
while(cur != null) {
stack.push(cur);
cur = cur.lchild;
}
RBTNode<T> node = stack.pop();
if(isBlack(node)) {
color = "BLACK";
}else {
color = "RED";
}
if(node.parent == null) {
System.out.println("根节点:"+node.key+" : "+color+" 父亲节点:"+node.parent);
}else {
System.out.println(node.key+" : "+color+" 父亲节点:"+node.parent.key);
}
if(node.rchild != null) {
cur = node.rchild;
}
}
}
//后序遍历
public void postorder_traversal() {
if(this.root == null)return;
RBTNode<T> head = this.root;
String color;
Stack<RBTNode<T>> stack = new Stack<RBTNode<T>>();
Stack<RBTNode<T>> stack2 = new Stack<RBTNode<T>>();
stack.push(head);
while(!stack.isEmpty()) {
RBTNode<T> node = stack.pop();
stack2.push(stack.pop());
if(node.rchild != null) stack.push(node.rchild);
if(node.lchild != null)stack.push(node.lchild);
}
while(!stack2.isEmpty()) {
RBTNode<T> node = stack.pop();
if(isBlack(node)) {
color = "BLACK";
}else {
color = "RED";
}
if(node.parent == null) {
System.out.println("根节点:"+node.key+" : "+color+" 父亲节点:"+node.parent);
}else {
System.out.println(node.key+" : "+color+" 父亲节点:"+node.parent.key);
}
}
}
//层序遍历
public void levelorder_traversal() {
if(this.root == null)return;
RBTNode<T> head = this.root;
String color;
Queue<RBTNode<T>> queue = new LinkedList<RBTNode<T>>();
queue.add(head);
while(!queue.isEmpty()) {
RBTNode<T> node = queue.poll();
if(isBlack(node)) {
color = "BLACK";
}else {
color = "RED";
}
if(node.parent == null) {
System.out.println(node.key+" : color("+color+") 父亲节点:"+node.parent);
}else {
System.out.println(node.key+" : color("+color+") 父亲节点:"+node.parent.key);
}
if(node.lchild != null)queue.add(node.lchild);
if(node.rchild != null)queue.add(node.rchild);
}
}
}
//红黑树测试类
public class RBTreeTest {
public static void main(String[] args) {
RBTree<Integer> tr = new RBTree<Integer>();
int[] arr = {10, 40, 30, 60, 90, 70, 20, 50, 80};
for (int key : arr) {
tr.insert(key);
}
System.out.println("红黑树的详细信息:");
tr.levelorder_traversal();
System.out.println("红黑树删除修复调整的信息:");
tr.remove(40);
System.out.println("红黑树删除后的树的调整:");
tr.levelorder_traversal();
}
}
红黑树测试代码截图如下:
我已经尽量简写了,有一些没必要的我就没写,测试也测试的比较简单。
请各位见谅时间问题呀,时间可真是好东西(感概一下!!!),
就请各位自己私下去多测多一点数据,更加深入了解红黑树的原理。感谢大家!
总结
红黑树的原理的详解和代码实现到这里就完结了!!!