红黑树是一种自平衡的二叉查找树,除了满足二叉查找树的性质外,还需要满足如下五个条件:
1. 节点是红色或黑色
2. 根节点为黑色
3. 所有叶子节点都是黑色
4. 每个红色节点都必须有两个黑色的子节点 也就是说在一条路径上不能出现相邻的两个红色结点。
5. 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点
要想知道为啥就要有对比的对象吧(平衡二叉树)
简介
AVL树是带有平衡条件的二叉查找树,一般是用平衡因子差值判断是否平衡并通过旋转来实现平衡,左右子树树高不超过1,和红黑树相比,AVL树是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差的绝对值不超过1)。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而旋转是非常耗时的,由此我们可以知道AVL树适合用于插入与删除次数比较少,但查找多的情况
答:由于维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,更多的地方是用追求局部而不是非常严格整体平衡的红黑树。当然,如果应用场景中对插入删除不频繁,只是对查找要求较高,那么AVL还是较优于红黑树。
答: 由 4. 每个红色节点都必须有两个黑色的子节点
5. 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点
可以假设 一条子树的黑节点个数为N
如果最高得子树每个黑节点后面都有一个红节点 那高度为2N
最短的就为N
所以 最差情况下高度比是2:1。
红黑树的维护操作操作有三个,分别是变色、左旋和右旋,
红黑树的插入、删除 有时会破坏树的结构 需要用这三个基本操作维护树的结构
变色操作比较简单,不改变该节点的值以及该节点在红黑树中的位置,仅改变该节点的颜色,红黑树颜色有两种,所以,变色分为两种,红色变为黑色和黑色变为红色(代码中用布尔类型数据表示)
private static final boolean RED = false;
private static final boolean BLACK = true;
对值为4的节点做左旋操作,就是将4节点的右子节点6节点上升,4节点作为6节点的左子节点,原6节点的左子节点作为4节点的右节点
代码如下
/*
* 对红黑树的节点(x)进行左旋转
*
* 左旋示意图(对节点x进行左旋):
* px px
* / /
* x y
* / \ --(左旋)-. / \ #
* lx y x ry
* / \ / \
* ly ry lx ly
*
*
*/
private void leftRotate(RBTNode<T> x) {
// 设置x的右孩子为y
RBTNode<T> y = x.right;
// 将 “y的左孩子” 设为 “x的右孩子”;
// 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
x.right = y.left;
if (y.left != null)
y.left.parent = x;
// 将 “x的父亲” 设为 “y的父亲”
y.parent = x.parent;
if (x.parent == null) {
this.mRoot = y; // 如果 “x的父亲” 是空节点,则将y设为根节点
} else {
if (x.parent.left == x)
x.parent.left = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
else
x.parent.right = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
}
// 将 “x” 设为 “y的左孩子”
y.left = x;
// 将 “x的父节点” 设为 “y”
x.parent = y;
}
对值为6的节点做右旋操作,就是将6节点的左子节点4节点上升,6节点作为4节点的右子节点,原4节点的右子节点作为6节点的左子节点
代码如下
/*
* 对红黑树的节点(y)进行右旋转
*
* 右旋示意图(对节点y进行左旋):
* py py
* / /
* y x
* / \ --(右旋)-. / \ #
* x ry lx y
* / \ / \ #
* lx rx rx ry
*
*/
private void rightRotate(RBTNode<T> y) {
// 设置x是当前节点的左孩子。
RBTNode<T> x = y.left;
// 将 “x的右孩子” 设为 “y的左孩子”;
// 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
y.left = x.right;
if (x.right != null)
x.right.parent = y;
// 将 “y的父亲” 设为 “x的父亲”
x.parent = y.parent;
if (y.parent == null) {
this.mRoot = x; // 如果 “y的父亲” 是空节点,则将x设为根节点
} else {
if (y == y.parent.right)
y.parent.right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
else
y.parent.left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
}
// 将 “y” 设为 “x的右孩子”
x.right = y;
// 将 “y的父节点” 设为 “x”
y.parent = x;
}
红黑树在新增节点过程中比较复杂,复杂归复杂它同样必须要依据上面提到的五点规范,同时由于规则1、2、3基本都会满足,下面我们主要讨论规则4、5。假设我们这里有一棵最简单的树,我们规定新增的节点为N、它的父节点为P、P的兄弟节点为U、P的父节点为G。
1、插入新节点总是红色节点 。
2、如果插入节点的父节点是黑色, 能维持性质 。
3、如果插入节点的父节点是红色, 破坏了性质. 故插入算法就是通过重新着色或旋转, 来维持性质
3.1、为跟节点
若新插入的节点N没有父节点,则直接当做根据节点插入即可,同时将颜色设置为黑色。
如下图
3.2、父节点为黑色
这种情况新节点N同样是直接插入,同时颜色为红色,由于根据规则四它会存在两个黑色的叶子节点,
值为null。同时由于新增节点N为红色,所以通过它的子节点的路径依然会保存着相同的黑色节点数,
同样满足规则5。
如图
3.3、若父节点P和P的兄弟节点U都为红色
对于这种情况若直接插入肯定会出现不平衡现象。怎么处理?P、U节点变黑、G节点变红。
这时由于经过节点P、U的路径都必须经过G所以在这些路径上面的黑节点数目还是相同的。
但是经过上面的处理,可能G节点的父节点也是红色,
这个时候我们需要将G节点当做新增节点递归处理。
直接插入的时候 (4是新插入的)
对树进行维护后(只牵扯到一次转换的)
对树进行维护后 (维护后的节点 与他的父节点不满足规则4)修改后4和6为红色相当于插入的是红色(如条件3.4 )对246左旋
3.4、若父节点P(图的6节点)为红色,叔父节点U(图上为空)为黑色或者缺少,
且新增节点N(图的7节点)为P节点的右孩子
对于这种情况我们先改变父节点和祖父的颜色 在对新增节点N、P进行一次左旋转。
3.5、父节点P为红色,叔父节点U为黑色或者缺少,新增节点N为父节点P左孩子
先对(父亲节点)7进行进行右旋 这个时候就变成3.4的情况了
针对于红黑树的增加节点而言,删除显得更加复杂,使原本就复杂的红黑树变得更加复杂。
删除节点和增加节点一样,同样是找到删除的节点,删除之后调整红黑树。
但是这里的删除节点(当节点左右子树都存在时)并不是直接删除,
而是通过走了“弯路”通过一种捷径来删除的:
找到被删除的节点D的子节点C,用C来替代D,不是直接删除D,因为D被C替代了,直接删除C即可。
所以这里就将删除父节点D的事情转变为了删除子节点C的事情,这样处理就将复杂的删除事件简单了。
子节点C的规则是:右分支最左边,或者左分支最右边的。
本文用的是 右分支最左边 代码如下
// 获取后继节点
replace = replace.right;
while (replace.left != null)
replace = replace.left; //取右节点的最小值
红-黑二叉树删除节点,最大的麻烦是要保持 各分支黑色节点数目相等。
因为是删除,所以不用担心存在颜色冲突问题——插入才会引起颜色冲突。
1、没有儿子,即为叶结点。直接把父结点的对应儿子指针设为NULL,删除儿子结点就OK了。
2、只有一个儿子。那么把父结点的相应儿子指针指向儿子的独生子,删除儿子结点也OK了。
3、有两个儿子。这种情况比较复杂,但还是比较简单。上面提到过用子节点C替代代替待删除节点D,
然后删除子节点C即可。 这个样问题就转化为只有一个儿子了,因为找到的C不能存在左儿子
1、下面要讲解的删除节点一定是实际要删除节点的后继节点(N),如前面提到的C。
2、下面提到的删除节点的树都是如下结构,该结构所选取的节点是待删除节点的右树的最左边子节
点。这里我们规定真实删除节点为N、真实删除节点的子节点S ,父节点为P、兄弟节点为W兄弟节点的
两个子节点为X1、X2。如下图(2.1)。
现在我们就上面提到的三种情况进行分析、处理。
这种情况对该节点(如果为红色)直接删除即可,不会影响树的结构。
如果为黑色就转化成情况二了;
这种情况先去判断子节点的颜色 当子节点为红色时子节点代替当前节点 ,
当子节点为黑色且删除节点为黑色 要去判断被删除节点的兄弟节点的颜色和兄弟节点子节点的颜色
有一下几种情况
*1.W是黑色 且其子节点都是黑色
* 2.W是黑色 其右节点是红色
* 3.W是黑色 其子节点左红右黑
* 4.W是红色
这种情况其父节点可红可黑,由于W为黑色,这样导致N子树相对于其兄弟W子树少一个黑色节点,
这时我们可以将W置为红色。这样,N子树与W子树黑色节点一致,保持了平衡。如下
将W由黑转变为红,这样就会导致新节点new N相对于它的兄弟节点会少一个黑色节点。
但是如果new N(原来N的父节点)为红色,
我们直接将new N(原来N的父节点)转变为黑色,
保持整棵树的平衡。否则情况3.1 会转变为情况3.2、3.3、3.4中的一种。
W为红色,那么其子节点X1、X2必定全部为黑色,父节点P也为黑色。
处理策略是:改变W、P的颜色,然后进行一次左旋转。这样处理就可以使得红黑性质得以继续保持。
操作:
将W由黑转变为红,这样就会导致新节点new N相对于它的兄弟节点会少一个黑色节点。
但是如果new x为红色,我们直接将new x转变为黑色,保持整棵树的平衡。
否则情况3.2 会转变为情况3.1、3.3、3.4中的一种。(因为x1的子树不知道)
针对这种情况是将节点W和其左子节点进行颜色交换,然后对W进行右旋转处理。
此时N的新兄弟X1(new w)是一个有红色右孩子的黑结点,于是将情况3转化为情况4.
交换W和父节点P(p的颜色没有要求)的颜色,同时对P进行左旋转操作。
这样就把左边缺失的黑色节点给补回来了。
同时将W的右子节点X2置黑。这样左右都达到了平衡。
可以用找后继节点的方式把两个子节点转化为一个子节点 ;
/**
* @author nwk
* 红黑树
* @version 1.0
* @date 2021/9/13 10:24
*/
public class RBTree<T extends Comparable<T>> {
private RBTNode<T> mRoot; // 根结点
private static final boolean RED = false;
private static final boolean BLACK = true;
public class RBTNode<T extends Comparable<T>> { //红黑树的节点
boolean color; // 颜色
T key; // 关键字(键值)
RBTNode<T> left; // 左孩子
RBTNode<T> right; // 右孩子
RBTNode<T> parent; // 父结点
public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
this.key = key;
this.color = color;
this.parent = parent;
this.left = left;
this.right = right;
}
public T getKey() {
return key;
}
public String toString() {
return ""+key+(this.color==RED?"(R)":"B");
}
}
public RBTree() {
mRoot=null;
}
private RBTNode<T> parentOf(RBTNode<T> node) {
return node!=null ? node.parent : null;
}
private boolean colorOf(RBTNode<T> node) {
return node!=null ? node.color : BLACK;
}
private boolean isRed(RBTNode<T> node) {
return ((node!=null)&&(node.color==RED)) ? true : false;
}
private boolean isBlack(RBTNode<T> node) {
return !isRed(node);
}
private void setBlack(RBTNode<T> node) {
if (node!=null)
node.color = BLACK;
}
private void setRed(RBTNode<T> node) {
if (node!=null)
node.color = RED;
}
private void setParent(RBTNode<T> node, RBTNode<T> parent) {
if (node!=null)
node.parent = parent;
}
private void setColor(RBTNode<T> node, boolean color) {
if (node!=null)
node.color = color;
}
/*
* 前序遍历"红黑树"
*/
private void preOrder(RBTNode<T> tree) {
if(tree != null) {
System.out.print(tree.key+" ");
preOrder(tree.left);
preOrder(tree.right);
}
}
public void preOrder() {
preOrder(mRoot);
}
/*
* 中序遍历"红黑树"
*/
private void inOrder(RBTNode<T> tree) {
if(tree != null) {
inOrder(tree.left);
System.out.print(tree.key+" ");
inOrder(tree.right);
}
}
public void inOrder() {
inOrder(mRoot);
}
/*
* 后序遍历"红黑树"
*/
private void postOrder(RBTNode<T> tree) {
if(tree != null)
{
postOrder(tree.left);
postOrder(tree.right);
System.out.print(tree.key+" ");
}
}
public void postOrder() {
postOrder(mRoot);
}
/*
* (递归实现)查找"红黑树x"中键值为key的节点
*/
private RBTNode<T> search(RBTNode<T> x, T key) {
if (x==null)
return x;
int cmp = key.compareTo(x.key);
if (cmp < 0)
return search(x.left, key);
else if (cmp > 0)
return search(x.right, key);
else
return x;
}
public RBTNode<T> search(T key) {
return search(mRoot, key);
}
/*
* (非递归实现)查找"红黑树x"中键值为key的节点
*/
private RBTNode<T> iterativeSearch(RBTNode<T> x, T key) {
while (x!=null) {
int cmp = key.compareTo(x.key);
if (cmp < 0)
x = x.left;
else if (cmp > 0)
x = x.right;
else
return x;
}
return x;
}
public RBTNode<T> iterativeSearch(T key) {
return iterativeSearch(mRoot, key);
}
/*
* 查找最小结点:返回tree为根结点的红黑树的最小结点。
*/
private RBTNode<T> minimum(RBTNode<T> tree) {
if (tree == null)
return null;
while(tree.left != null)
tree = tree.left;
return tree;
}
public T minimum() {
RBTNode<T> p = minimum(mRoot);
if (p != null)
return p.key;
return null;
}
/*
* 查找最大结点:返回tree为根结点的红黑树的最大结点。
*/
private RBTNode<T> maximum(RBTNode<T> tree) {
if (tree == null)
return null;
while(tree.right != null)
tree = tree.right;
return tree;
}
public T maximum() {
RBTNode<T> p = maximum(mRoot);
if (p != null)
return p.key;
return null;
}
/*
* 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
*/
public RBTNode<T> successor(RBTNode<T> x) {
// 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
if (x.right != null)
return minimum(x.right);
// 如果x没有右孩子。则x有以下两种可能:
// (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
// (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
RBTNode<T> y = x.parent;
while ((y!=null) && (x==y.right)) {
x = y;
y = y.parent;
}
return y;
}
/*
* 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。
*/
public RBTNode<T> predecessor(RBTNode<T> x) {
// 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
if (x.left != null)
return maximum(x.left);
// 如果x没有左孩子。则x有以下两种可能:
// (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
// (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
RBTNode<T> y = x.parent;
while ((y!=null) && (x==y.left)) {
x = y;
y = y.parent;
}
return y;
}
/*
* 对红黑树的节点(x)进行左旋转
*
* 左旋示意图(对节点x进行左旋):
* px px
* / /
* x y
* / \ --(左旋)-. / \ #
* lx y x ry
* / \ / \
* ly ry lx ly
*
*
*/
private void leftRotate(RBTNode<T> x) {
// 设置x的右孩子为y
RBTNode<T> y = x.right;
// 将 “y的左孩子” 设为 “x的右孩子”;
// 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
x.right = y.left;
if (y.left != null)
y.left.parent = x;
// 将 “x的父亲” 设为 “y的父亲”
y.parent = x.parent;
if (x.parent == null) {
this.mRoot = y; // 如果 “x的父亲” 是空节点,则将y设为根节点
} else {
if (x.parent.left == x)
x.parent.left = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
else
x.parent.right = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
}
// 将 “x” 设为 “y的左孩子”
y.left = x;
// 将 “x的父节点” 设为 “y”
x.parent = y;
}
/*
* 对红黑树的节点(y)进行右旋转
*
* 右旋示意图(对节点y进行左旋):
* py py
* / /
* y x
* / \ --(右旋)-. / \ #
* x ry lx y
* / \ / \ #
* lx rx rx ry
*
*/
private void rightRotate(RBTNode<T> y) {
// 设置x是当前节点的左孩子。
RBTNode<T> x = y.left;
// 将 “x的右孩子” 设为 “y的左孩子”;
// 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
y.left = x.right;
if (x.right != null)
x.right.parent = y;
// 将 “y的父亲” 设为 “x的父亲”
x.parent = y.parent;
if (y.parent == null) {
this.mRoot = x; // 如果 “y的父亲” 是空节点,则将x设为根节点
} else {
if (y == y.parent.right)
y.parent.right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
else
y.parent.left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
}
// 将 “y” 设为 “x的右孩子”
x.right = y;
// 将 “y的父节点” 设为 “x”
y.parent = x;
}
/*
* 红黑树插入修正函数
*
* 在向红黑树中插入节点之后(失去平衡),再调用该函数;
* 目的是将它重新塑造成一颗红黑树。
*1.Z是根节点(即Z插入前是一颗空树)
* 2.Z的叔节点是红色的
* 3.Z的叔节点是黑色的,并且局部呈现三角行(左右三角)
* 3.Z的叔节点是黑色的,并且局部呈现直线角行(左右直线)
*
* 参数说明:
* node 插入的结点
*
*/
private void insertFixUp(RBTNode<T> node) {
RBTNode<T> parent, gparent;
// 若“父节点存在,并且父节点的颜色是红色”
while (((parent = parentOf(node))!=null) && isRed(parent)) {
gparent = parentOf(parent);
//若“父节点”是“祖父节点的左孩子”
if (parent == gparent.left) {
// Case 1条件:叔叔节点是红色
RBTNode<T> uncle = gparent.right;
if ((uncle!=null) && isRed(uncle)) {
setBlack(uncle);
setBlack(parent);
setRed(gparent);
node = gparent;
continue;
}
// Case 2条件:叔叔是黑色,且当前节点是右孩子
if (parent.right == node) {
RBTNode<T> tmp;
leftRotate(parent);
tmp = parent;
parent = node;
node = tmp;
}
// Case 3条件:叔叔是黑色,且当前节点是左孩子。
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
} else { //若“z的父节点”是“z的祖父节点的右孩子”
// Case 1条件:叔叔节点是红色
RBTNode<T> uncle = gparent.left;
if ((uncle!=null) && isRed(uncle)) {
setBlack(uncle);
setBlack(parent);
setRed(gparent);
node = gparent;
continue;
}
// Case 2条件:叔叔是黑色,且当前节点是左孩子
if (parent.left == node) {
RBTNode<T> tmp;
rightRotate(parent);
tmp = parent;
parent = node;
node = tmp;
}
// Case 3条件:叔叔是黑色,且当前节点是右孩子。
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
}
}
// 将根节点设为黑色
setBlack(this.mRoot);
}
/*
* 将结点插入到红黑树中
*
* 参数说明:
* node 插入的结点 // 对应《算法导论》中的node
*/
private void insert(RBTNode<T> node) {
int cmp;
RBTNode<T> y = null;
RBTNode<T> x = this.mRoot;
// 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
while (x != null) {
y = x;
cmp = node.key.compareTo(x.key); //当前key h和插入的key进行比较
if (cmp < 0)
x = x.left;
else
x = x.right;
}
node.parent = y;
if (y!=null) {
cmp = node.key.compareTo(y.key); //判断插入到左边还是右边
if (cmp < 0)
y.left = node;
else
y.right = node;
} else {
this.mRoot = node;
}
// 2. 设置节点的颜色为红色
node.color = RED;
// 3. 将它重新修正为一颗二叉查找树
insertFixUp(node);
}
/*
* 新建结点(key),并将其插入到红黑树中
*
* 参数说明:
* key 插入结点的键值
*/
public void insert(T key) {
RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);
// 如果新建结点失败,则返回。
if (node != null)
insert(node);
}
/*
* 红黑树删除修正函数
*
* 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
* 目的是将它重新塑造成一颗红黑树。
*
*
*
* 参数说明:
* node 待修正的节点
*/
private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
RBTNode<T> other;
while ((node==null || isBlack(node)) && (node != this.mRoot)) {
if (parent.left == node) {//删除的节点是左节点
other = parent.right;
if (isRed(other)) {
// Case 1: x的兄弟w是红色的
setBlack(other);
setRed(parent);
leftRotate(parent);
other = parent.right;
}
if ((other.left==null || isBlack(other.left)) &&
(other.right==null || isBlack(other.right))) {
// Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
setRed(other);
node = parent;
parent = parentOf(node);
} else {
if (other.right==null || isBlack(other.right)) {
// Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
setBlack(other.left);
setRed(other);
rightRotate(other);
other = parent.right;
}
// Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.right);
leftRotate(parent);
node = this.mRoot;
break;
}
} else { //删除的节点是右节点
other = parent.left;
if (isRed(other)) {
// Case 1: x的兄弟w是红色的
setBlack(other);
setRed(parent);
rightRotate(parent);
other = parent.left;
}
if ((other.left==null || isBlack(other.left)) &&
(other.right==null || isBlack(other.right))) {
// Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
setRed(other);
node = parent;
parent = parentOf(node);
} else {
if (other.left==null || isBlack(other.left)) {
// Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
setBlack(other.right);
setRed(other);
leftRotate(other);
other = parent.left;
}
// Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.left);
rightRotate(parent);
node = this.mRoot;
break;
}
}
}
if (node!=null)
setBlack(node);
}
/*
* 删除结点(node),并返回被删除的结点
*
* 参数说明:
* node 删除的结点
*/
private void remove(RBTNode<T> node) {
RBTNode<T> child, parent;
boolean color;
// 被删除节点的"左右孩子都不为空"的情况。
if ( (node.left!=null) && (node.right!=null) ) {
// 被删节点的后继节点。(称为"取代节点")
// 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。
RBTNode<T> replace = node;
// 获取后继节点
replace = replace.right;
while (replace.left != null)
replace = replace.left;
// "node节点"不是根节点(只有根节点不存在父节点)
if (parentOf(node)!=null) {
if (parentOf(node).left == node)
parentOf(node).left = replace;
else
parentOf(node).right = replace;
} else {
// "node节点"是根节点,更新根节点。
this.mRoot = replace;
}
// child是"取代节点"的右孩子,也是需要"调整的节点"。
// "取代节点"肯定不存在左孩子!因为它是一个后继节点。
child = replace.right;
parent = parentOf(replace);
// 保存"取代节点"的颜色
color = colorOf(replace);
// "被删除节点"是"它的后继节点的父节点"
if (parent == node) {
parent = replace;
} else {
// child不为空
if (child!=null)
setParent(child, parent);
parent.left = child;
replace.right = node.right;
setParent(node.right, replace);
}
replace.parent = node.parent;
replace.color = node.color;
replace.left = node.left;
node.left.parent = replace;
if (color == BLACK) //红色直接删除 黑色删除后维护
removeFixUp(child, parent);
node = null;
return ;
}
if (node.left !=null) {
child = node.left;
} else {
child = node.right;
}
parent = node.parent;
// 保存"取代节点"的颜色
color = node.color;
if (child!=null)
child.parent = parent;
// "node节点"不是根节点
if (parent!=null) {
if (parent.left == node)
parent.left = child;
else
parent.right = child;
} else {
this.mRoot = child;
}
if (color == BLACK)
removeFixUp(child, parent);
node = null;
}
/*
* 删除结点(z),并返回被删除的结点
*
* 参数说明:
* tree 红黑树的根结点
* z 删除的结点
*/
public void remove(T key) {
RBTNode<T> node;
if ((node = search(mRoot, key)) != null)
remove(node);
}
/*
* 销毁红黑树
*/
private void destroy(RBTNode<T> tree) {
if (tree==null)
return ;
if (tree.left != null)
destroy(tree.left);
if (tree.right != null)
destroy(tree.right);
tree=null;
}
public void clear() {
destroy(mRoot);
mRoot = null;
}
/*
* 打印"红黑树"
*
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
private void print(RBTNode<T> tree, T key, int direction) {
if(tree != null) {
if(direction==0) // tree是根节点
System.out.printf("%2d(B) is root\n", tree.key);
else // tree是分支节点
System.out.printf("%2d(%s) is %2d's %6s child\n", tree.key, isRed(tree)?"R":"B", key, direction==1?"right" : "left");
print(tree.left, tree.key, -1);
print(tree.right,tree.key, 1);
}
}
public void print() {
if (mRoot != null)
print(mRoot, mRoot.key, 0);
}
}
测试代码(测试和)
public class RBTreeTest {
private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
public static void main(String[] args) {
int i, ilen = a.length;
RBTree<Integer> tree=new RBTree<Integer>();
System.out.printf("== 原始数据: ");
for(i=0; i<ilen; i++)
System.out.printf("%d ", a[i]);
System.out.printf("\n");
for(i=0; i<ilen; i++) {
tree.insert(a[i]);
// 设置mDebugInsert=true,测试"添加函数"
System.out.printf("== 添加节点: %d\n", a[i]);
System.out.printf("== 树的详细信息: \n");
tree.print();
System.out.printf("\n");
}
System.out.printf("== 前序遍历: ");
tree.preOrder();
System.out.printf("\n== 中序遍历: ");
tree.inOrder();
System.out.printf("\n== 后序遍历: ");
tree.postOrder();
System.out.printf("\n");
System.out.printf("== 最小值: %s\n", tree.minimum());
System.out.printf("== 最大值: %s\n", tree.maximum());
System.out.printf("== 树的详细信息: \n");
tree.print();
System.out.printf("\n");
// 设置mDebugDelete=true,测试"删除函数"
for(i=0; i<ilen; i++)
{
tree.remove(a[i]);
System.out.printf("== 删除节点: %d\n", a[i]);
System.out.printf("== 树的详细信息: \n");
tree.print();
System.out.printf("\n");
}
// 销毁二叉树
tree.clear();
}
}