目录
一、基本介绍
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条性质,也就不再是一颗红黑树了,而是一颗普通的树。
而通过旋转,可以使这颗树重新成为红黑树。
简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。
下面分别对红黑树的基本操作进行介绍。
红黑树的基本类的实现代码
/* 用内部类的方式实现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. 常用方法
左旋
左旋的实现代码
//左旋
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 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);
}
}
添加修正
//插入修正算法,红黑树的核心算法之一
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(pare