算法学习之数据结构之红黑树(1)

算法学习之数据结构之红黑树(一)

  一,红黑树性质。

  由于二叉查找树知道,一个高度为h的二叉查找树可以实现任何一种基本的动态几何操作,如search,insert,minimum,delete,successor等操作,其时间都是O(h),这样树的高度低了就会执行的比较快,但是当树的高度较高时,操作的性能可能不比链表好。红黑树是许多“平衡的”查找树中的一种,他能保证在最坏的情况下,基本的动态集合操作的时间为O(lgn)。

  红黑树的性质。红黑树是一种二叉查找树,,每一个节点增加一个存储位表示节点的颜色,为红色或者黑色。红黑树确保没有一条路径会比其他路径长出两倍。因而是接*衡的。
  树的每个节点包含五个域:color, parent, key, left, right。一个二查找树如果满足如下红黑性质,则为一个红黑树。
  1)每个节点或是红的,或是黑的。  
  2)根节点是黑。
  3)每个叶节点(NIL)是黑的。
  4)如果一个节点是红的, 则它的两个孩子都是黑的。
  5)对每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点。
  从某个节点x出发到达一个叶节点的任意一条路径上,黑色节点的个数成为该节点x的黑高度。
  一个有n个内节点的红黑树的高度至多为2lg(n+1)
  

  二、红黑树旋转。

  当对红黑树上进行insert和delete时,对树进行了修改,结果可能会违反红黑树的性质,所以需要改变树中的节点的颜色和指针结构,即需要进行旋转。
  左旋,假设它的右孩子节点y不是nil[T];x可以为树内任意右孩子不是nil[T]的节点,左旋以x和y之间的链为轴进行,它使y成为该子树新的根,x成为y的左孩子节点,而y的左孩子节点为x的右孩子。
  伪代码如下:
left-rotate(T, x)
y = right[x]
right[x] = left[y] // y的左子树变为x的右子树
parent[left[y]] = x
parent[y] = parent[x] // x的父亲节点是y的父亲节点
if parent[y] == null
    then root[T] = y // x的根节点是根节点则置为y
    else if x == left[parent[x]] // 如果x是其父节点的左孩子节点,则将y置为x父节点的左孩子节点否则置为其父节点的右孩子节点
             then left[parent[x]] = y
             else right[parent[x]] = y
left[y] = x // y的左孩子结点是x
parent[x] = y // x的父亲节点是y

  三、红黑树插入。

  
  分为两部分,先把节点插入红黑树中,并着为红色,然后进行修补以保持红黑树的性质。先看前一部分的伪代码,和二叉查找树的插入大部分类似。

rb-insert(T, z)
y = null
x = root[T]
while x != null
    do y = x
        if(key[x] < key[z])
            then x = right[x]
            else x = left[x]
parent[z] = y
if(y == null) //树是空的
    then root[T] = z
    else if key[z] < key[y]
        then left[y] = z
        else right[y] = z
left[z] = nil[T]
right[z] = nil[T]
color[z] = RED // 着色为红色
rb-insert-fixup(T, z)

  就最后四行和二叉查找树不一样,其他的都一样,并着为红色,然后进行修补以保持红黑树的性质,接下来看rb-insert-fixup(T, z),将插入的节点z着为红色以后,可能破坏的性质有性质2和性质4,所以我们要做的就是恢复性质2和性质4.先大致说一下判断情况:
1,首先让一个指针y指向z的叔叔节点(z是要删除的节点)。
2,如果y是红色的,CASE1。则使z的父亲节点和叔叔节点改为黑色,z的祖父及诶单改为红色。然后z转移到z的祖父节点。
3,当y的颜色是黑色时,
①.首先判断z是否是其父亲结点的右儿子,若是,则调整为左儿子(用旋转)。 CASE2。
②.其次使z的父亲结点颜色为黑,z的祖父结点颜色为红,并以z的祖父结点为轴右旋,CASE3。看伪代码吧。
伪代码如下:

rb-insert-fixup(T, z)
while color[p[z]] = RED
    do if parent[z] == left[parent[parent[z]]] // z节点的父亲节点是其祖父节点的左孩子节点
             then y = right[parent[parent[z]]] // y为z节点的祖父节点的右孩子节点,即叔叔节点
                    if color[y] == RED // 叔叔节点为红色 CASE1
                        then color[parent[z]] = black // 父亲节点黑色
                               color [y] = black // 叔叔节点黑色
                               color[parent[parent[z]] = red // 祖父节点红色
                               z = parent[parent[z]]
                        esle if z == right[parent[z]] // 叔叔节点是黑的,且z为父节点的右孩子节点 CASE2
                                 then z = parent[z]
                                        left-rotate(T, z)  // 左旋转
                         // CASE3
                        color[parent[z]] = black //把父节点着黑色
                        color[parent[parent[z]]] = red //原来的祖节点着红色
                        right-rotate(T, z)
         else // z节点的父亲节点是其祖父节点的右孩子节点
                操作和左孩子节点一样
color[root[T]] = black // 最后把根节点置为黑色

具体解释如下:

  根据Z的父节点是Z的祖节点的左子节点还是右子节点,分为两组对称的情况,每组有3种情况。以Z的父节点是Z祖节点的左子节点为例:  

算法学习之数据结构之红黑树(1)

第一种:Z的“叔父”节点是红色。

 

             算法学习之数据结构之红黑树(1) 

 

如上图所示,在这种情况下,将父、叔节点都着为黑色,再将子树根节点着为红色,那么子树的黑高度没有发生改变,而且红黑性质得得到了调整。此时,再将Z指向子树的根节点,向上递归恢复红黑特性。

第二种:Z的“叔父”节点是黑色的,Z的父节点的右子节点。 
算法学习之数据结构之红黑树(1)

 

如上图,将Z本身与其父节点进行一次左旋,让Z指向原来的父节点,就可以调整为情况三,下面看情况三。

第三种:Z的“叔父”节点是黑色的,Z的父节点的左子节点。
 

算法学习之数据结构之红黑树(1)

如上图,将Z的父节点与祖节点进行一次右旋,并把父节点着黑色,原来的祖节点着红色。这些子树的红黑特性得到了恢复,而且子树的黑高度没有变化。另外,由于子树根节点已经是黑色了(这个节点不会出现父子同为红色的问题了),所以不必再向上递归了,此时整个树的红黑特性都已经是正确的了。

由于红黑树的删除比较复杂,放在下一篇。

网上发现不错的学习资料,分享一下:黑树算法的层层剖析与逐步实现