第13章 红黑树
红黑树是一种平衡搜索树中的一种,他可以保证在最坏情况下基本动态集合操作时间复杂度为O(logn)。所以,在学习红黑树之前,我需要先去调研一下平衡树;
先看一下平衡树的定义:平衡树,即平衡二叉树(Balanced Binary Tree):它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL(平衡查找树)、替罪羊树、Treap、伸展树等。 最小二叉平衡树的节点的公式为: F(n)=F(n-1)+F(n-2)+1 ,这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。
下面这段介绍很生动:对一棵查找树(search tree)进行查询/新增/删除 等动作, 所花的时间与树的高度h 成比例, 并不与树的容量 n 成比例。如果可以让树维持矮矮胖胖的好身材, 也就是让h维持在O(lg n)左右, 完成上述工作就很省时间。能够一直维持好身材, 不因新增删除而长歪的搜寻树, 叫做balanced search tree(平衡树)。所以,可以看出,构造平衡树的动机就在于:对于普通二叉搜索树,虽然最好情况和一般情况都趋近于O(lg n),但是最坏情况非常糟糕,会存在树的高度h=n的畸形情况,此时树的性能非常糟糕。
下面介绍两种常见的平衡树算法基本概念(百度得来。。。):
红黑树:
了解了基本概念和特性,下面开始介绍红黑树了。
红黑树通过对任何一条从根到叶子的简单路径上各个结点的颜色进行约束,确保没有一条路径会比其他路径长处2倍,因而近似于平衡的。
书中每个及诶单包含5个属性:color,key,left,right,p。如果一个节点没有子节点或父结点,则该节点相应指针属性为null。把这些Null视为指向二叉搜索树的叶结点(外部结点)的指针,把带有关键字的结点视为数的内部节点。
红黑树这种有一个概念:哨兵,代表Null。对于一颗红黑树T,哨兵T.null是一个与树中普通节点具有相同属性的对象。她的color属性为black,其他属性任意。所有指向null的指针都用指向哨兵T.null的指针替换。
从某个节点x出发(不含该节点)到达一个叶结点的任意一条简单路径上的黑色结点个数成为该节点的黑高,记为bh(x)。由于上述性质 ,可以得出红黑树的黑高为其根结点的黑高。
一颗有n个内部节点的红黑树的高度至多为2lg(n+1),即 h <= 2lg(n+1)。所以红黑树是一种好的搜索树。
从性质3还可得出,从根节点到叶结点(不包括根节点)的任何一条简单路径上都至少有一半的结点为黑色。因此,根的黑高至少为h/2(h为树高)。于是有 n >= 2.^(h/2) - 1.
综上所述,一颗红黑树上的查找删除等操作时间复杂度都在O(lgn)范围内。
红黑树保持平衡的操作就是旋转操作:左旋和右旋,下面是代码实现:
public void leftRotate(RedBlackTree tree, RBNode x) {
RBNode y = x.right;// 找到要旋转结点的右子节点
x.right = y.left;// 让y节点的左子节点旋转到x的右子节点处,注意:此处仍符合搜索树的大小关系
if (y.left != tree.Nil) {//如果y的左子节点不为空,修改其父结点为x
y.left.p = x;
}
y.p = x.p;//修改y结点的父结点为x的父结点
if (x.p == tree.Nil) {// 如果x是根结点,则y变为根结点
tree.root = y;
} else if (x == x.p.left) {// 如果x是其父结点的左子节点,则令x父结点的左结点为y
x.p.left = y;
} else {// 如果x是其父结点的右子节点,则令x父结点的右结点为y
x.p.right = y;
}
y.left = x;//旋转x为y的左结点
x.p = y;//修改x的父结点为y
}
public void rightRotate(RedBlackTree tree, RBNode y) {
RBNode x = y.left;
y.left = x.right;
if (x.right != tree.Nil) {
x.right.p = y;
}
x.p = y.p;
if (y.p == tree.Nil) {// 如果y是根结点
tree.root = x;
} else if (y == y.p.left) {// 如果y是其父结点的左子节点
y.p.left = x;
} else {// 如果y是其父结点的右子节点
y.p.right = x;
}
x.right = y;
y.p = x;
}
下面附加一张旋转图还辅助理解:
下面就介绍红黑树中的插入操作: