Java数据结构----树--红黑树

来源:互联网 发布:ubuntu 14.04 selinux 编辑:程序博客网 时间:2024/06/06 03:35

1、概念

  红黑树(Red-Black Tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

  红黑树是一种很有意思的平衡检索树。它的统计性能要好于平衡二叉树(有些书籍根据作者姓名,Adelson-Velskii和Landis,将其称为AVL-树),因此,红黑树在很多地方都有应用。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持)。

2、背景

    二叉搜索树在最坏的情况下可能会变成一个链表(当所有节点按从小到大的顺序依次插入后)。这种低效产生的原因是树没有维持一定的平衡性,要提高搜索效率,就要想办法来维持树左边的平衡,也就是要尽时降低树的高度,可行的做法就是用一些策略在每次修改树的内容之后都调整树的结构,使之满足一定的平衡条件。其中一种满足一定平衡条件而且目前应用广泛的是红黑树。
     红黑树是一种特定类型的二叉树,它是在计算机科学中用来组织数据比如数字的块的一种结构。所有数据块都存储在节点中。这些节点中的某一个节点总是担当启始位置的功能,它不是任何节点的儿子;我们称之为根节点或根。

3、属性

红黑树的每个节点上的属性除了有一个key、3个指针:parent、left、right以外,还多了一个属性:color。它只能是两种颜色:红或黑,当然也可以再加一些以key来索引的数据。而红黑树除了具有二叉搜索树的所有性质之外,还具有以下5点性质:

1. 节点是红色或黑色。
2. 根节点是黑色。
3. 空节点是黑色的(红黑树中,根节点的parent以及所有叶节点left、right都不指向NULL,而是指向一个定义好的空节点,这样可以保持算法的一致性,简化算法)。
4. 红色节点的父、左子、右子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
5. 在任何一棵子树中,每一条从根节点向下走到空节点的路径上包含的黑色节点数量都相同。
如下图就是一棵红黑树:

                

   上面约束强制了红黑树的关键属性: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值都要求与树的高度成比例的最坏情况时间,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。
   要知道为什么这些特性确保了这个结果,注意到属性4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据属性5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。
    在很多树数据结构的表示中,一个节点有可能只有一个儿子,而叶子节点包含数据。用这种范例表示红黑树是可能的,但是这会改变一些属性并使算法复杂。为此,本文中我们使用 "nil 叶子" 或"空(null)叶子",如上图所示,它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,导致了这些树好像同上述原则相矛盾,而实际上不是这样。与此有关的结论是所有节点都有两个儿子,尽管其中的一个或两个可能是空叶子。

小记:
    1.  在一棵红黑树中null节点是很多的,如果每个null节点都用一个真的节点的话那就太浪费空间了,我们可以对所有的null节点都使用同一个节点,这样可以简化算法又能节约空间。
    2.  由于红黑树的结构比较复杂,代码的书写一定要常规,比如一般null的left,right和parent都指向树的根节点,但是一般不应该用null节点来索引根节点或是做相应的判断,因为在一些地方我们为了保持算法的一致性,可能改变null节点的left,right,parent的指向。
    3.  这里的红黑树实现并不是真的想拿来应用,而是没有写代码来练习。具体参数TreeMap源码分析
    4. 节点x的黑高度:从某个节点x到达一个叶结点的任意一条路径上包含的黑色结点(包括叶结点)数量。用bh(x)表示。另外规定叶结点的黑高度为0。

定理:一棵含有n个内结点的红黑树的树高至多为2log(n+1)
证明:先证以某一节点x为根的子树中至少包含2^bh(x) - 1个内节点。用归纳法:
  (1)x的高度为0,则x为一叶节点,以x为根的子树中包含2^bh(x) - 1 = 2^0 - 1 = 0;
  (2)考虑一个高度为正值的节点x,它是个内节点,且有两个子女,每个子女根据其自身的颜色是红或黑而有黑高度bh(x)或bh(x) - 1,由归纳假设,每个子女至少包含2^(bh(x) - 1) - 1个内节点。所以,以节点x为根的子树中至少包含(2^(bh(x) - 1) - 1) + (2^(bh(x) - 1) - 1) + 1 = 2^bh(x) - 1。命题得证。
设h为树的高度,根据性质4,从根到叶节点(不包括根)的任一条简单路径上,至少有一半的节点必是黑的。从而,根的黑高度至少为h / 2;故有 n>= 2^(h / 2) - 1 有lg(n + 1) >= h / 2 或(h <= 2lg(n +1))。
所以,命题得证。
由这个定理可知,动态集合操作Search, Minimum, Maximum, Successor, Predecessor可用红黑树在O(lg n)时间内实现。

一、插入

首先是二叉搜索树的插入步骤,把新节点z插入到某一个叶节点的位置上,把z的颜色设成红色。如果z的父节点也是红色,我们要执行下面一个迭代的过程,称为InsertFixup,来修补这棵红黑树。
在InsertFixup中,每一次迭代的开始,指针z一定都指向一个红色的节点。如果z->p是黑色,那我们就大功告成了;如果z->p是红色,显然这就违返了红黑的树性质,那么我们要想办法把z或者z->p变成黑色,但这要建立在不破坏红黑树的其他性质的基础上。
在每一次迭代中,我们可能遇到以下三种情况。


二、删除

红黑树中删除一个节点z的方法也是首先按部就班二叉搜索树的过程。如果删除的节点是黑色的,那么红黑树的性质就被破坏了。这时我们就要执行一个称为DeleteFixup的过程,来修补这棵树。
一个节点被删除之后,一定有一个它的子节点代替了它的位置。我们就设指针x指向这个代替位置的节点。显然,如果x是红色的,那么我们只要把它设成黑色,它所在的路径上就重新多出了一个黑色节点,那么红黑树的性质就满足了。然而,如果x是黑色的,那我们就要假想x上背负了2个单位的黑色。那么红黑树的性质也同样不破坏,但是我们要找到某一个红色的节点,把x上“超载”的这1个单位的黑色丢给它,这样才算完成.deleteFixup做的就是这个工作。
DeleteFixup同样是一个循环迭代的过程。每一次迭代开始时,如果指针x指向一个红色节点,把它设成黑色即告终。如果x黑色,那么我们就会面对以下4种情况。

0 0
原创粉丝点击