在掌握了二叉搜索树的基础之上,下面我们来学习一下红黑树。
为什么需要红黑树呢?它到底好在哪里?为了回答这个问题,我们需要反过来思考一下一般的二叉搜索树,它的增删查找速度是O(h)。h越大,二叉树高度越高,那么操作的速度越慢,如果h接近n,那么效率将变的非常低。我们都希望树的高度近似logn,而红黑树就是为了解决这个问题而诞生的。
先看看红黑树除了满足基本的二叉搜索树之外,还需要具备哪些条件:
1)每个结点或是红色的,或是黑色的。
2)根结点是黑色的。
3)每个叶结点是黑色的
4)如果一个结点是红色的,那么它的孩子节点一定是黑色的
5)对于每个结点,从该结点到叶子结点的简单路径上,黑色结点的个数相同。
先来看看红黑树的样子:
有了这个性质,我们可以得到:对于一颗有n个结点的红黑树的高度最多为2log(n+1),同时从根结点到叶子结点的最长路径<2*最短路径。为什么呢?因为要求路径上黑色结点的个数必须相同,可想而知,最短的路径就是全黑结点,最长的路径是黑红相间的,同时头尾都是黑色,所以有上面的公式。
一. 旋转
到这里我们有必要了解一下二叉搜索树的一个有趣的操作,旋转。绕着一个结点进行下面的旋转变换不改变二叉搜索树的任何性质。这个在平衡二叉搜索树中很常用。
注意观察图中的叶子节点变换前后的变化。
下面是左旋和右旋的代码:
在放代码之前要先说明一点,红黑树中的叶子节点和我们之前看到的null节点不一样,它是有颜色的,同时树中所有的叶子节点和根的父节点都是同一个对象nil。nil就相当于我们在二叉搜索树中的null对象。
private void leftRotate(TreeNode xNode)
{
TreeNode yNode = xNode.getrChild();
xNode.setrChild(yNode.getlChild());
if (yNode.getlChild() != nil)
yNode.getlChild().setParent(xNode);
transplant(xNode, yNode);
yNode.setlChild(xNode);
xNode.setParent(yNode);
}
private void rightRotate(TreeNode yNode)
{
TreeNode xNode = yNode.getlChild();
yNode.setlChild(xNode.getrChild());
if (xNode.getrChild() != nil)
xNode.getrChild().setParent(yNode);
transplant(yNode, xNode);
xNode.setrChild(yNode);
yNode.setParent(xNode);
}
二. 插入
红黑树的插入操作和一般的二叉搜索树很类似。但是不同的是,插入结点为红色,以后还需要保持红黑树的颜色性质。这个就比较复杂了,它根据插入结点的位置情况,分成几种情况:
下面的情况都是插入结点和父节点同时红色的情况,不然如果父节点是黑色,那么就满足要求不用处理了。在插入操作中,插入结点的叔节点的颜色是关键。
x结点是插入的结点,y结点是x的叔节点。
第一种情况:x和y同时红色,不管x是左右节点都要进行同样的变化----将父节点和叔节点变黑,爷爷节点变红。
下面就要判断爷爷节点的颜色是否有问题了,继续看属于哪种情况。
下面两种情况同时针对x的父节点是爷爷节点的左节点而言的,如果是右节点,只需要反正做就可以了。
第二种情况:y节点是黑色,并且x节点是右节点
操作---对x节点的父节点左旋,将变为情况3一起处理。
第三种情况: y节点是黑色的,并且x节点是左节点
操作----对换x的父节点和爷爷节点的颜色,对x结点的爷爷节点右旋。
具体的insert操作代码:
private void insert(TreeNode zNode)
{
//yNode是为了找到要插入点的父节点
TreeNode yNode = nil;
TreeNode xNode = root;
while (xNode != nil) {
yNode = xNode;
if (zNode.getKey() <= xNode.getKey())
xNode = xNode.getlChild();
else
xNode = xNode.getrChild();
}
zNode.setParent(yNode);
if (yNode == nil)
root = zNode;
else if (zNode.getKey() <= yNode.getKey())
yNode.setlChild(zNode);
else
yNode.setrChild(zNode);
zNode.setlChild(nil);
zNode.setrChild(nil);
zNode.setColor(RED);
insertFixUp(zNode);
}
private void insertFixUp(TreeNode zNode)
{
while (zNode.getParent().getColor() == RED) {
if (zNode.getParent() == zNode.getParent().getParent().getlChild()) {
//yNode是zNode的叔节点
TreeNode yNode = zNode.getParent().getParent().getrChild();
if (yNode.getColor() == RED) {
zNode.getParent().setColor(BLACK);
yNode.setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
zNode = zNode.getParent().getParent();
} else if (zNode == zNode.getParent().getrChild()) {
zNode = zNode.getParent();
leftRotate(zNode);
} else {
zNode.getParent().setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
rightRotate(zNode.getParent().getParent());
}
} else {
TreeNode yNode = zNode.getParent().getParent().getlChild();
if (yNode.getColor() == RED) {
zNode.getParent().setColor(BLACK);
yNode.setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
zNode = zNode.getParent().getParent();
} else if (zNode == zNode.getParent().getlChild()) {
zNode = zNode.getParent();
rightRotate(zNode);
} else {
zNode.getParent().setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
leftRotate(zNode.getParent().getParent());
}
}
}
root.setColor(BLACK);
}
三. 删除操作
删除操作和插入操作一样,都只需要花费O(logn)的时间。也是一样,在完成常规的删除操作后我们需要保持红黑树的性质不变。
需要强调的是,如果删除节点的后继节点颜色是黑色并且不是叶子节点,那么这就意味着所有原来包含后继节点的节点路径上黑色结点数目少一个。我们必须将这个性质保存不变,怎么变呢?我们统一将该节点的黑色加到它的右节点上。这样如果其右节点颜色是红,那么逻辑上现在就变为红黑色,如果其右节点颜色是黑色,那么为了保持性质不变,现在它的颜色变为黑黑色(双重黑)。我们要做的,就是解决双重黑的复杂情况。
也人说后继节点的情况很简单啊,因为它需要满足红黑树的特性,所以它的右节点要么是叶子节点,要么是红色节点,那么直接处理不是可以了吗?为什么要分那么多种情况呢?那是因为当它的右节点是叶子节点时,变为双重黑,在这样的情况下特别复杂,我们专门针对这种情况的处理才有了下面的几种4中情况图:
这里我们要用x结点的兄弟节点作为判断:
图中x节点是删除节点的后继节点的右节点,或者是删除节点的子节点(子节点中一个为nil)。
图中写的很详细,这里就不多说了。(Bing大法好啊)
下面是删除delete操作的java代码:
private void delete(TreeNode zNode)
{
//yNode要么被删除,要么被移至zNode位置
TreeNode yNode = zNode;
int yOriginalColor = yNode.getColor();
//yNode的继承结点
TreeNode xNode;
if (zNode.getlChild() == nil) {
xNode = zNode.getrChild();
transplant(zNode, xNode);
} else if (zNode.getrChild() == nil) {
xNode = zNode.getlChild();
transplant(zNode, xNode);
} else {
yNode = findMin(zNode.getrChild());
yOriginalColor = yNode.getColor();
xNode = yNode.getrChild();
if (yNode.getParent() == zNode)
xNode.setParent(yNode);
else {
transplant(yNode, yNode.getrChild());
yNode.setrChild(zNode.getrChild());
yNode.getrChild().setParent(yNode);
}
transplant(zNode, yNode);
yNode.setlChild(zNode.getlChild());
yNode.getlChild().setParent(yNode);
yNode.setColor(zNode.getColor());
}
if (yOriginalColor == BLACK)
deleteFixUp(xNode);
}
private void deleteFixUp(TreeNode xNode)
{
while (xNode != root && xNode.getColor() == BLACK) {
if (xNode == xNode.getParent().getlChild()) {
//wNode是xNode的兄弟节点
TreeNode wNode = xNode.getParent().getrChild();
if (wNode.getColor() == RED) {
wNode.setColor(BLACK);
xNode.getParent().setColor(RED);
leftRotate(xNode.getParent());
wNode = xNode.getParent().getrChild();
}
if (wNode.getlChild().getColor() == BLACK && wNode.getrChild().getColor() == BLACK) {
wNode.setColor(RED);
xNode = xNode.getParent();
} else if (wNode.getrChild().getColor() == BLACK) {
wNode.getlChild().setColor(BLACK);
wNode.setColor(RED);
rightRotate(wNode);
wNode = xNode.getParent().getrChild();
} else {
wNode.setColor(xNode.getParent().getColor());
xNode.getParent().setColor(BLACK);
wNode.getrChild().setColor(BLACK);
leftRotate(xNode.getParent());
xNode = root;
}
} else {
TreeNode wNode = xNode.getParent().getlChild();
if (wNode.getColor() == RED) {
wNode.setColor(BLACK);
xNode.getParent().setColor(RED);
rightRotate(xNode.getParent());
wNode = xNode.getParent().getlChild();
}
if (wNode.getrChild().getColor() == BLACK && wNode.getlChild().getColor() == BLACK) {
wNode.setColor(RED);
xNode = xNode.getParent();
} else if (wNode.getlChild().getColor() == BLACK) {
wNode.getrChild().setColor(BLACK);
wNode.setColor(RED);
leftRotate(wNode);
wNode = xNode.getParent().getlChild();
} else {
wNode.setColor(xNode.getParent().getColor());
xNode.getParent().setColor(BLACK);
wNode.getlChild().setColor(BLACK);
rightRotate(xNode.getParent());
xNode = root;
}
}
}
xNode.setColor(BLACK);
}
下面是整个红黑树类的代码:
/**
*
*/
package binaryTree;
/**
* @author freestyle458
*
*/
public class RedBlackTree {
private static final int RED = 0;
private static final int BLACK = 1;
private class TreeNode {
private int key;
private int color;
private TreeNode lChild;
private TreeNode rChild;
private TreeNode parent;
public TreeNode() {
key = 0;
color = BLACK;
lChild = null;
rChild = null;
parent = null;
}
public TreeNode(int key)
{
this();
this.key = key;
}
public TreeNode(int value, int color)
{
this();
this.key = value;
this.color = color;
}
public int getKey()
{
return key;
}
public void setKey(int key)
{
this.key = key;
}
public int getColor()
{
return color;
}
public void setColor(int color)
{
this.color = color;
}
public TreeNode getlChild()
{
return lChild;
}
public void setlChild(TreeNode lChild)
{
this.lChild = lChild;
}
public TreeNode getrChild()
{
return rChild;
}
public void setrChild(TreeNode rChild)
{
this.rChild = rChild;
}
public TreeNode getParent()
{
return parent;
}
public void setParent(TreeNode parent)
{
this.parent = parent;
}
@Override
public String toString()
{
// TODO Auto-generated method stub
String result = "";
if (lChild == nil)
result += "[leftChild:nil, ";
else {
result += "[leftChild:" + lChild.getKey() + ", ";
}
result += "key:" + key +", ";
result += "color:";
if (color == 0)
result += "red, ";
else
result += "black, ";
if (parent != nil)
result += "parent:" + parent.getKey() + ", ";
else
result += "parent:nil, ";
if (rChild == nil)
result += "rightChild:nil]";
else {
result += "rightChild:" + rChild.getKey() + "]";
}
return result;
}
}
private TreeNode nil = new TreeNode(-1, BLACK);
private TreeNode root;
public RedBlackTree()
{
root = nil;
}
public RedBlackTree(int value)
{
TreeNode tNode = new TreeNode(value, BLACK);
root = tNode;
root.setParent(nil);
root.setlChild(nil);
root.setrChild(nil);
}
public void preorder()
{
preorder(root);
}
private void preorder(TreeNode tNode)
{
if (tNode != nil) {
preorder(tNode.getlChild());
System.out.println(tNode);
preorder(tNode.getrChild());
}
}
private void leftRotate(TreeNode xNode)
{
TreeNode yNode = xNode.getrChild();
xNode.setrChild(yNode.getlChild());
if (yNode.getlChild() != nil)
yNode.getlChild().setParent(xNode);
transplant(xNode, yNode);
yNode.setlChild(xNode);
xNode.setParent(yNode);
}
private void rightRotate(TreeNode yNode)
{
TreeNode xNode = yNode.getlChild();
yNode.setlChild(xNode.getrChild());
if (xNode.getrChild() != nil)
xNode.getrChild().setParent(yNode);
transplant(yNode, xNode);
xNode.setrChild(yNode);
yNode.setParent(xNode);
}
//把yNode节点移到xNode结点的位置上
private void transplant(TreeNode xNode, TreeNode yNode)
{
if (xNode.getParent() == nil)
root = yNode;
else if (xNode == xNode.getParent().getlChild()) {
xNode.getParent().setlChild(yNode);
} else {
xNode.getParent().setrChild(yNode);
}
yNode.setParent(xNode.getParent());
}
public void insert(int value)
{
TreeNode tNode = new TreeNode(value);
insert(tNode);
}
private void insert(TreeNode zNode)
{
//yNode是为了找到要插入点的父节点
TreeNode yNode = nil;
TreeNode xNode = root;
while (xNode != nil) {
yNode = xNode;
if (zNode.getKey() <= xNode.getKey())
xNode = xNode.getlChild();
else
xNode = xNode.getrChild();
}
zNode.setParent(yNode);
if (yNode == nil)
root = zNode;
else if (zNode.getKey() <= yNode.getKey())
yNode.setlChild(zNode);
else
yNode.setrChild(zNode);
zNode.setlChild(nil);
zNode.setrChild(nil);
zNode.setColor(RED);
insertFixUp(zNode);
}
private void insertFixUp(TreeNode zNode)
{
while (zNode.getParent().getColor() == RED) {
if (zNode.getParent() == zNode.getParent().getParent().getlChild()) {
//yNode是zNode的叔节点
TreeNode yNode = zNode.getParent().getParent().getrChild();
if (yNode.getColor() == RED) {
zNode.getParent().setColor(BLACK);
yNode.setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
zNode = zNode.getParent().getParent();
} else if (zNode == zNode.getParent().getrChild()) {
zNode = zNode.getParent();
leftRotate(zNode);
} else {
zNode.getParent().setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
rightRotate(zNode.getParent().getParent());
}
} else {
TreeNode yNode = zNode.getParent().getParent().getlChild();
if (yNode.getColor() == RED) {
zNode.getParent().setColor(BLACK);
yNode.setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
zNode = zNode.getParent().getParent();
} else if (zNode == zNode.getParent().getlChild()) {
zNode = zNode.getParent();
rightRotate(zNode);
} else {
zNode.getParent().setColor(BLACK);
zNode.getParent().getParent().setColor(RED);
leftRotate(zNode.getParent().getParent());
}
}
}
root.setColor(BLACK);
}
public boolean delete(int value)
{
TreeNode tNode = search(root, value);
if (tNode == nil)
return false;
delete(tNode);
return true;
}
private TreeNode findMin(TreeNode rootNode)
{
while (rootNode.getlChild() != nil)
rootNode = rootNode.getlChild();
return rootNode;
}
private void delete(TreeNode zNode)
{
//yNode要么被删除,要么被移至zNode位置
TreeNode yNode = zNode;
int yOriginalColor = yNode.getColor();
//yNode的继承结点
TreeNode xNode;
if (zNode.getlChild() == nil) {
xNode = zNode.getrChild();
transplant(zNode, xNode);
} else if (zNode.getrChild() == nil) {
xNode = zNode.getlChild();
transplant(zNode, xNode);
} else {
yNode = findMin(zNode.getrChild());
yOriginalColor = yNode.getColor();
xNode = yNode.getrChild();
if (yNode.getParent() == zNode)
xNode.setParent(yNode);
else {
transplant(yNode, yNode.getrChild());
yNode.setrChild(zNode.getrChild());
yNode.getrChild().setParent(yNode);
}
transplant(zNode, yNode);
yNode.setlChild(zNode.getlChild());
yNode.getlChild().setParent(yNode);
yNode.setColor(zNode.getColor());
}
if (yOriginalColor == BLACK)
deleteFixUp(xNode);
}
private void deleteFixUp(TreeNode xNode)
{
while (xNode != root && xNode.getColor() == BLACK) {
if (xNode == xNode.getParent().getlChild()) {
//wNode是xNode的兄弟节点
TreeNode wNode = xNode.getParent().getrChild();
if (wNode.getColor() == RED) {
wNode.setColor(BLACK);
xNode.getParent().setColor(RED);
leftRotate(xNode.getParent());
wNode = xNode.getParent().getrChild();
}
if (wNode.getlChild().getColor() == BLACK && wNode.getrChild().getColor() == BLACK) {
wNode.setColor(RED);
xNode = xNode.getParent();
} else if (wNode.getrChild().getColor() == BLACK) {
wNode.getlChild().setColor(BLACK);
wNode.setColor(RED);
rightRotate(wNode);
wNode = xNode.getParent().getrChild();
} else {
wNode.setColor(xNode.getParent().getColor());
xNode.getParent().setColor(BLACK);
wNode.getrChild().setColor(BLACK);
leftRotate(xNode.getParent());
xNode = root;
}
} else {
TreeNode wNode = xNode.getParent().getlChild();
if (wNode.getColor() == RED) {
wNode.setColor(BLACK);
xNode.getParent().setColor(RED);
rightRotate(xNode.getParent());
wNode = xNode.getParent().getlChild();
}
if (wNode.getrChild().getColor() == BLACK && wNode.getlChild().getColor() == BLACK) {
wNode.setColor(RED);
xNode = xNode.getParent();
} else if (wNode.getlChild().getColor() == BLACK) {
wNode.getrChild().setColor(BLACK);
wNode.setColor(RED);
leftRotate(wNode);
wNode = xNode.getParent().getlChild();
} else {
wNode.setColor(xNode.getParent().getColor());
xNode.getParent().setColor(BLACK);
wNode.getlChild().setColor(BLACK);
rightRotate(xNode.getParent());
xNode = root;
}
}
}
xNode.setColor(BLACK);
}
private TreeNode search(TreeNode rootNode, int value)
{
if (rootNode == nil) {
return nil;
} else {
if (rootNode.getKey() == value)
return rootNode;
else if (rootNode.getKey() < value)
return search(rootNode.getrChild(), value);
else
return search(rootNode.getlChild(), value);
}
}
public static void main(String[] args)
{
RedBlackTree rbTree = new RedBlackTree();
rbTree.preorder();
System.out.println("----------------------");
int[] values = new int[]{1, 2, 4, 5, 7, 8, 14, 15, 6, 16};
for (int i = 0; i < values.length; i++) {
rbTree.insert(values[i]);
}
rbTree.preorder();
System.out.println("-----------------------");
System.out.println("delete 5:" + rbTree.delete(5));
rbTree.preorder();
}
}
main函数运行结果:
我这里用的是中序遍历,是安顺序输出的,这里可以看到,插入和删除的操作是正确的!!!
如果各位朋友有空,可以试着画一下图形,我特意删除的根结点,然后看看这种情况是不是你所想的那样。
如果是的话,那再删除15,看看呢