zoukankan      html  css  js  c++  java
  • 数据结构的扩张

    前言:通常我们会遇到一些问题,采用一些标准的数据结构,如双链表、散列表或二叉查找数时,不能够满足操作要求,需要对这些数据结构进行扩张,添加一些额外的信息使得能够完成新的操作。附加的信息需要对数据结构的某些操作进行调整,这个是非常关键的步骤,决定着数据结构扩张是否能够实现。本章主要讨论了红黑树结构的扩张,介绍了两种扩张方式。第一种方式扩张使得红黑色能够支持动态集合上顺序统计,快速找出集合中第i小的数,或给出某个元素在集合的全序中的排名。第二种方式扩张使得红黑色能够进行区间操作,可以很快地找到集合中覆盖的区间。关于红黑色请参考第13章,http://www.cnblogs.com/Anker/archive/2013/01/30/2882773.html

    1、动态顺序统计

      在第九章介绍了顺序统计的概念,大概的意思是在包含有n个元素的集合中,第i个顺序统计量指的是该集合中第i小的元素。在一个无序的集合中,任意顺序统计量都可以在O(n)时间内找到,详细情况可以参考http://www.cnblogs.com/Anker/archive/2013/01/25/2877311.html。书中在此基础上修改红黑树的结构,使得任意的顺序统计量都可以再O(lgn)时间内确定。向红黑树的结构中添加一个size域,表示包含自身节点的当前节点的子树节点的数目。这样修改后可以快速支持顺序统计量操作,将这种修改后的红黑树叫做:顺序统计量树T。修改后的结构如下所示:

    复制代码
    struct RBTreeNode
    {
       int key;
       int color;
       struct RBTreeNode *parent;
       struct RBTreeNode *left;
       struct RBTreeNode *right;
       int size;  
    };
    复制代码

    例如给定红黑树的一个节点x,则size[x] = size[left[x]]+size[right[x]]+1。size[x]为包含以x为根的子树的节点数(包含x本身),即子树的大小。如果哨兵定义为0,即设置size[nil[T]]=0

    下面给出一个修改后的红黑树的例子,如下图所示:

      红黑树是二叉排序树,按照中序遍历从小到大输出红黑树中的关键字。从图中可以看出,添加size域后,很方便看出每个节点的子树的节点数目(包含自身节点)。书中在后面讨论这种结构的操作,分别讨论如下:

    (1)检索具有给定排序的元素

      过程OS_SELECT(x,i)返回一个指向以x为根的子树中包含第小关键字的结点的指针,即为了找出顺序统计量树T中的第i小关键字,可以调用OS_SELECT(root[T],i)。书中给出了伪代码如下:

    复制代码
    OS_SELECT(x,i)
        r = size[left[x]]+1; //先计算x的处于的位置
        if i = r         //x正好是第i小的关键字
            then return x;
        else if i < r   //x比第i关键字大,则在其左子树查找
            then return OS_SELECT(left[x],i)
        else return OS_SELECT(right[x],i-r)  //x比第i关键字小,则在其右子树查找
    复制代码

      该过程类似二分查找,每一次递归调用都在顺序统计数中下降一层,故最坏情况下OS_SELECT的总时间与树的高度成正比,红黑树的高度为lgn。故OS_SELECT的运行时间为:O(lgn)。

    (2)确定一个元素的秩(位置)

      给定指向一顺序统计树T中节点x的指针,求x在顺序统计树中序遍历得到的线性序中的位置。书中给出了OS_RANK(T,x)过程的伪代码:

    复制代码
    OS_RANK(T,x)
        r = size[left[x]]+1;   //获取以x为根子树中x的位置(中序遍历)
        y = x;
        while y != root[T]    //从下向上直到根节点
              do if y = right[p[y]]   //如果是右子树
                      then  r = r + size[left[p[y]]]+1; 
              y = p[y]; //向上移动
        return r;
    复制代码

    从程序总可以看出当y == root[T]时候循环终止,此时以y为根的子树是课完整树,此时r值是这颗整树中key[x]的秩。while循环中的每一次迭代花O(1)时间,且y在每次迭代中沿树上升一层,故在最坏情况下0S_RANK的运行时间与树的高度成正比:对含n个节点的顺序统计树时间为O(lgn)。

    (3)对子树规模的维护

      在红黑树中添加size域后,能够通过OS_SELECT和OS_RANK迅速计算出所需的顺序统信息。通过修改红黑树的插入和删除操作,在此过程是通过旋转来修改size域。关于这部分需要在红黑树的基础上进行改进,比较复杂,暂时没有实现。

    2、如何扩张数据结构

    对一种数据结构的扩张过程分为四个步骤:

    1)选择基础数据结构

    2)确定要在基础数据结构中添加哪些信息

    3)验证可用基础数据结构上的基本修改操作来维护这些新添加的信息

    4)设计新的操作

      书中给出了对红黑树进行扩张的定理,并给出了证明,这个看的时候有些难度,暂时跳过了。大概意思就是说当红黑树被选作基础数据结构时,某些类型的附加信息总是可以用插入和删除来进行有效地维护。

    3、区间树

      这小结讲的是扩张红黑树以支持由区间构成的动态集合上的操作。区间可以很方便的表示各占用一段连续时间的一些事情。区间树是一种动态集合进行维护的红黑树,该集合中的每个元素x都包含在一个区间int[x]。区间树支持下列操作:

    INTERVAL_INSERT(T,x):将包含区间域int的元素x插入到区间树T中

    INTERVAL_DELETE(T,X):从区间树T中删除元素x

    INTERVAL_SEARCH(T,i):返回一个指向区间树T中元素x的指针,使int[x]与i重叠,若集合中无此元素存在,则返回nil[T]。

    修改红黑树得到的区间树如下图所示:

    从图可以看出,对区间树以每个节点的左端点值进行中序变量即可得到有序的序列。有了区间树的结果就很容易实现其相关操作。

    红黑树

    摘要:

      红黑树是一种二叉查找树,但在每个结点上增加了一个存储位表示结点的颜色,可以是RED或者BLACK。通过对任何一条从根到叶子的路径上各个着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。本章主要介绍了红黑树的性质、左右旋转、插入和删除。重点分析了在红黑树中插入和删除元素的过程,分情况进行详细讨论。一棵高度为h的二叉查找树可以实现任何一种基本的动态集合操作,如SEARCH、PREDECESSOR、SUCCESSOR、MIMMUM、MAXMUM、INSERT、DELETE等。当二叉查找树的高度较低时,这些操作执行的比较快,但是当树的高度较高时,这些操作的性能可能不比用链表好。红黑树(red-black tree)是一种平衡的二叉查找树,它能保证在最坏情况下,基本的动态操作集合运行时间为O(lgn)。本章内容有些复杂,看了两天,才大概清楚其插入和删除过程,日后需要经常回顾,争取完全消化掉。红黑树的用途非常广泛,例如STL中的map就是采用红黑树实现的,效率非常之高,有机会可以研究一下STL的源代码。

    1、红黑树的性质

      红黑树中的每个结点包含五个域:color、key、left、right和parent。如果某结点没有一个子结点或父结点,则该结点相应的指针parent域包含值为NIL(NIL并是是空指针,此处有些迷惑,一会解释)。把NIL视为指向红黑树的外结点(叶子)的指针,而把带关键字的结点视为红黑树的内结点。红黑树结点结构如下所示:

    复制代码
     1 #define RED  0
     2 #define BLACK 1
     3 struct RedBlackTreeNode
     4 { 
     5     T key;
     6     struct RedBlackTreeNode * parent;
     7     struct RedBlackTreeNode * left;
     8     struct RedBlackTreeNode * right;
     9     int color;
    10 };
    复制代码

    红黑树的性质如下:

    (1)每个结点或是红色,或是黑色。

    (2)根结点是黑色。

    (3)每个叶子结点(NIL)是黑色。

    (4)如果有一个结点是红色,则它的两个儿子都是黑色。

    (5)对每个结点,从该结点到其孙子结点的所有路径上包含相同数目的黑色结点。

    如下图是一棵红黑树:

    从图可以看出NIL不是空指针,而是一个叶子结点。实际操作的时候可以将NIL视为哨兵,这样便于对黑红色进行操作。红黑树的操作主要是对内部结点操作,因为内部结点存储了关键字的值。书中为了便于讨论,忽略了叶子结点的,如是上图红黑树变成如下图所示:

      书中给出了黑高度的概念:从某个结点x出发(不包含该结点)到达一个叶子结点的任意一条路径上,黑色结点的个数称为该结点的黑高度。由红黑树的性质(5)可知,从该结点出发的所有下降路径都有相同的黑色结点个数。红黑树的黑高度定义为其根结点的黑高度。

      书中给出了一个引理来说明为什么红黑树是一种好的查找树,并对引理进行了证明(采用归纳法进行证明的,需要很强的归纳推理知识,正是我的不足之处,看书的痛苦在于此)。

    引理:一棵有n个内结点的红黑树的高度之多为2lg(n+1)。

    2、旋转

      在红黑树上进行结点插入和删除操作时,会改变树的结构形状,导致结果可能不满足了红黑树的某些性质,为了保证每次插入和删除操作后,仍然能报维持红黑树的性质,需要改变树中某些结点的颜色和指针结构。其中的指针结构的改变通过旋转完成的。书中给出了两种旋转:左旋转和右旋转。如下图是旋转过程:

      从图可以得出左右旋转的过程,假设对某个结点x进行左旋转,y是x的右孩子,则左旋转过程为:以x和y之间的链为“支轴”进行的,使得x的右孩子为y的左孩子,y的父节点为x的父节点,y的左孩子为x。书中给出了左旋转的伪代码如下:

    复制代码
     1 LEFT_ROTATE(T,x)
     2    y = right[x]   //获取右孩子
     3    rihgt[x] = left[y]  //设置x的右孩子为y的左孩子
     4    if left[y] != NIL
     5        then parent[left[x]] = x
     6     parent[y] = parent[x]  //设置y的父节点为x的父节点
     7     if parent[x] == NIL
     8        then root[T] = y
     9        else if x==left[parent[x]
    10               then left[parent[x]] = y
    11               else  right[[parent[x]] = y
    12     left[y] = x  //设置y的左孩子为x
    13     parent[x] =y
    14 
    15    
    复制代码

    假设对某个结点y进行右旋转,x是y的左孩子,则左旋转过程为:y的左孩子设置为x的右孩子,将x的父节点设置为y的父节点,x的右孩子设置为y。书中并没有给出右旋转的伪代码,参照左旋转的伪代码很容易实现:

    复制代码
     1  RIGHT_ROTATE(T,y)
     2      x = left[y]    //获取左孩子
     3      left[y] = right[x] //设置y的左孩子为x的右孩子
     4      if right[x] != NIL
     5         then parent[right[x]] = y
     6      parent[x] = parent[y]  //设为x的父节点为y的父结点
     7      if parent[y] == NIL
     8          then root = x
     9          else if y== left[parent[y]]
    10                then left[parent[y]] = x
    11                else  right[parent[y]] = x
    12      right[x] = y //设置x的右孩子为y
    13      parent[y] = x
    复制代码

     为了更好的理解旋转操作,书中给出了一个左旋转的例如,如下图所示:

    3、插入

      红黑树插入一个新结点的过程RB_INSERT是在二叉查找树插入过程的基础上改进的,先按照二叉排序的插入过程插入到红黑树中,然后将新插入的结点标记为红色(疑问:为什么是红色,而不是黑色呢?),然后调用一个辅助的过程RB_INSERT_FIXUP来调整结点并重新着色,使得满足红黑树的性质。关于二叉查找树的插入过程可以参考上一篇日志:http://www.cnblogs.com/Anker/archive/2013/01/28/2880581.html。书中给出了RB_INSERT的伪代码:

    复制代码
     1 RB_INSERT(T,z)
     2   y = NIL
     3   x =root(T)
     4   while x != NIL
     5        do y=x
     6            if key[z]<key[x]
     7              then x=left[x]
     8              else  x=right[x]
     9   parent[z] = y
    10   if y =NIL
    11      then root =z
    12      else if key[z] < key[y]
    13             then left[y] =z
    14             else  right[y] =z
    15    left[z] = NIL
    16    right[z] =NIL
    17    color[z] = RED  //新插入结点标记为红色
    18    RB_INSERT_FIXUP(T,z)  //进行调整,使得满足红黑树性质
    复制代码

      红黑树的插入过程最主要的是RB_INSERT_FIXUP过程,书中发了很大的篇幅进行介绍。首先分析了当插入一个新的结点后,会破坏红黑树的哪些性质,然后针对可能的破坏性质进行分类讨论并给出了给出了解决办法。因为每次插入的新元素标记为RED,这样可能性质2(根节点为黑色)和性质4(一个红结点的左右孩子都是黑色的)被破坏。例如下图插入一个新结点,破坏了性质4。

          如果每次插入新的结点z导致红黑树性质被破坏,则之多只有一个性质被破坏,并且不是性质2就是性质4。违反性质2是因为z是根且为红色,违反性质4是因为z和其父节点parent[z]都是红色的。

      如果性质2被违反了,则红色的根必定是新增的结点z,它是树中唯一的内结点,由于z的父接点和两个子女都是NIL(黑色),不违反性质4。违反性质2在整个插入过程中只有这一次。所以对于违反性质2的结点,直接将其结点变成黑色即可。

      剩下的问题是对于违反性质4的处理,在插入新结点z之前,红黑树的性质没有被破坏。插入结点z后违反性质4,必定是因为z和其父亲结点parent[z]都是红色的,此时只违反性质4,而没有违反其他性质。假设新插入结点z,导致红黑树性质4被破坏,此时z和其父节点parent[z]都是红色,由于在插入结点z之前红黑树的性质没有被破坏,parent[z]是红色,很容易推出z的祖父结点parent[parent[z]]必定是黑色。此时根据parent[z]是parent[parent[z]]的左孩子还是右孩子进行讨论。因为左右之间是对称的,书中只给出了parent[z]作为parent[parent[z]]的左孩子进行讨论的,然后给出了三种可能的情况。

    情况1):z的叔叔结点y是红色的

      此时parent[z]和y都是红色的,解决办法是将z的父节点parent[z]和叔叔结点y都着为黑色,而将z的祖父结点parent[parent[z]]着为红色,然后从祖父结点parent[parent[z]]继续向上判断是否破坏红黑树的性质。处理过程如下图所示:

    情况2):z的叔叔y是黑色的,而且z是右孩子

    情况3):z的叔叔y是黑色的,而且z是左孩子

      情况2和情况3中y都是黑色的,通过z是左孩子还是右孩子进行区分的。可以将情况2通过旋转为情况3。情况2中z是右孩子,旋转后成为情况3,使得z变为左孩子,可以在parent[z]结点出使用一次左旋转来完成。无论是间接还是直接的通过情况2进入到情况3,z的叔叔y总是黑色的。在情况3中,将parent[z]着为黑色,parent[parent[z]]着为红色,然后从parent[parent[z]]处进行一次右旋转。情况2、3修正了对性质4的违反,修正过程不会导致其他的红黑性质被破坏。修正过程如下图所示:

      给一个完整的例子来说明插入过程,如下图所示:

      书中给出了RB_INSERT_FIXUP的伪代码,伪代码中只给出了z的父节点为左孩子的情况,为右孩子的情况与左孩子的情况是对称的,只需将左孩子中的right换成left即可。

    复制代码
     1 RB_INSERT_FIXUP(T,z)
     2          while color[parent[z]] = RED
     3            do if parent[z] == left[parent[parent[z]]]
     4                   then y = right[parent[parent[z]]]
     5                        if color[y] == RED    //情况1,z的叔叔为红色
     6                             then color[parent[z]] = BLACK
     7                                  color[y] = BLACK
     8                                  color[parent[parent[z]]=RED 
    9 z= parent[parent[z]] 10 else if z == right[parent[z]] //情况2,z的叔叔为黑色,z为右孩子 11 then z = parent[z] 12 LEFT_ROTATE(T,z) 13 color[parent[z]]=BLACK //情况3,z的叔叔为黑色,z为左孩子 14 color[parent[parent[z]] = RED 15 RIGHT_ROTATE(T, parent[parent[z]]) 16 else (same as then clause with “right” and “left” exchanged) 17 color(root(T)) = BLACK; //将根结点设置为黑色
    复制代码

    4、删除

       删除过程最复杂,看了好多遍才明白个大概,需要反复看,多想删除过程中会破坏哪些性质,然后又针对性的去调整。

      红黑树删除结点过程是在二叉查找树删除结点过程的基础改进的。与二叉查找树类似,删除的结点分为三种情况:<1>无左右孩子、<2>有左孩子或者右孩子、<3>既有树=左孩子又有右孩子。删除过程可以参考前一篇日志:http://www.cnblogs.com/Anker/archive/2013/01/28/2880581.html。红黑树在删除结点后需要检查是否破坏了红黑树的性质。如果删除的结点y是红色的,则删除后的树仍然是保持红黑树的性质,因为树中各个结点的黑高度没有改变,不存在两个相邻(父结点和子结点)的红色结点,y是红色不可能是根,所有根仍然是黑色。如果删除的结点z是黑色的,则这个是破坏了红黑树的性质,需要调用RB_DELETE_FIXUP进行调整。从删除结点y的唯一孩子结点x或者是NIL处开始调整。书中给出了RB_DELETE的伪代码:

    复制代码
     1 RB_DELETE(T,z)
     2      if left[z] ==NIL or right[z] == NIL
     3         then y=z
     4         else  y=TREE_SUCCESSOR(z)
     5     if left[y] != NIL
     6         then x=left[y]
     7         else  x=right[y]
     8     parent[x] = parent[y]
     9     if p[y] ==NIL
    10        then root[T] =x
    11        else if y = left[[prarnt[y]]
    12                    then left[parent[y]] = x
    13                    else  right[parent[y]] =x
    14      if y!=z
    15          then key[z] = key[y]
    16                copy y's data into z
    17      if color[y] == BLACK    //当被删除结点为黑色时候进行调整
    18          then RB_DELETE_FIXUP(T,x)
    19       return y
    复制代码

      书中分析了被删除结点y是黑色会产生的问题:首先,如果y是根,而y的一个红色孩子变成了新根,则违反了性质2。其次,如果x和parent[y](此时parent[x] = parent[y])都是红色,就违反了性质4。第三,删除y将会导致先前包含y的任何路径上黑结点个数减少1,违反了性质5。书中给出了解决第三个问题的办法:将结点x设为还有额外的一重黑色(此处看的不是很明白,我的理解是是不管是x是什么颜色,将x增加了额外一重黑色,这样可以保证黑结点数目增加1个),即将任意包含结点x的路径上黑结点个数加1,这样可以保证性质5成立。当将黑色结点y被删除时,将其黑色“下推”至其子结点,导致问题变成为结点x可能即不是红,又不是黑,从而违反性质1。因为给x增加了一种颜色,即结点x是双重黑色或者是红黑色。这样就分别给包含x的路径上黑结点个数贡献2个或1个。但是x的color属性仍然是RED(如果x是红黑的)或BLACK(如果x是双重黑色)。换而言之,一个结点额外的黑色反映在x指向它,而不是它的color属性。

      过程RB_DELETE_FIXUP恢复性质1,2,4。对于恢复性质2、4很简单,因为x是红色,所有直接将x结点着为黑色即可。书中着重介绍了如何恢复性质1。此时x是黑色,需要根据x是左孩子还是右孩子两种情况进行恢复,因为左右是对称的,书中只给出了x是左孩子的恢复过程。将x作为第一个额外的黑色结点,从x结点开始循环,将额外的黑色结点沿着树向上移,直到:

    (1)x指向一个红黑结点,此时将x单独着为黑色。

    (2)x指向根,这时可以简单地消除那个额外的黑色,或者

    (3)做必要的旋转和颜色改变

    在循环过程中,x总是指向具有双重黑色的那个非根结点。设w是x的兄弟结点,因为x是双重黑色的,故w不可能是NIL。书中分四种情况讨论:

    情况1:x的兄弟w是红色的

              此时因为x是双重黑色,贡献两个黑色结点,所有w必有黑色孩子。此时将w着为黑色,parent[x]为红色,在对parent[x]做一次左旋转。此时x的新兄弟w是黑色,这样将情况1转换为情况2、3或4。情况1的处理过程下图所示:

    情况2:x的兄弟w是黑色的,而且w的两个孩子都是黑色的。

         处理过程是从x和w上去掉一重黑色,即x只有一重黑色而w着为红色,给x的父节点parent[x]添加额外黑色。处理过程如下图所示:

    情况3:x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的

           交换w和其左孩子left[w]的颜色,并对w进行右旋转。旋转后x的新兄弟w是一个有红色右孩子的黑结点,转换成了情况4。处理过程如下图所示:

    情况4:x的兄弟w是黑色的,而且w的右孩子是红色的。

      执行过程是将w的颜色设置为parent[x]的颜色,将parent[x]的颜色设置为黑色,将w的右孩子着为黑色,然后在parent[x]做一次右旋,最后将x设置为根root。处理过程如下图所示:

    书中给出了RB_DELETE_FIXUP的伪代码:

    复制代码
     1 RB_DELETE_FIXUP(T,x)
     2    while x!= root[T] and color[x] ==BLACK
     3          do if x == left[parent[x]]
     4                then w = right[parent[x]]
     5                     if color[w] == RED  //case 1 x的兄弟w是红色的
     6                        then color[w] = BLACK
     7                             color[parent[x]] = RED
     8                             LEFT_ROTATE(T,PARENT[x])
     9                             w = right[parent[x]]
    10                        if color[left[w]] == BLACK and color[right[w]] = BLACK
    11                           then color[w] = RED  //case 2
    12                             x = parent[x]
    13                            else if color[right[w]] =BLACK
    14                                   then color[left[w]] = BLACK //case 3
    15                                        color[w] = RED
    16                                        RIGHT_ROTATE(T,w)
    17                                        w = right[parent[x]]
    18                                color[w] = color[parent[x]] //case 4
    19                                color[parent[x]] = BLACK
    20                                color[right[w]] = BLACK
    21                                LEFT_ROTATE(T,parent[x])
    22                                x=root(T)
    23              else(same as then clasue with “right” and “left” exchanged)
    24      color[x]=BLACK
    复制代码

    5、编程实现

      这一章看了两天,宏观上把握了红黑树的插入和删除操作,中间还有细节问题需要思考。看完后要实现才能消化,于是我采用C++语言设计了简单的红黑树结点和红黑树类,设计的类如下所示:

    复制代码
     1 static const int RED = 0;
     2 static const int BLACK = 1;
     3 
     4 template <class T>
     5 class RedBlackTreeNode
     6 {
     7 public:
     8     RedBlackTreeNode():key(T()),parent(NULL),left(NULL),right(NULL),color(BLACK){}
     9     T key;
    10     RedBlackTreeNode<T>* parent;
    11     RedBlackTreeNode<T>* left;
    12     RedBlackTreeNode<T>* right;
    13     int color;
    14 };
    15 
    16 template <class T>
    17 class RedBlackTree
    18 {
    19 public:
    20     RedBlackTree();
    21     int search_element(const T& k) const;
    22     int get_minmum(T& retmin)const;
    23     int get_maxmum(T& retmax)const;
    24     int get_successor(const T& k,T& ret) const;
    25     int get_predecessor(const T& k,T& ret) const;
    26     int insert_key(const T& k);
    27     int delete_key(const T& k);
    28     void inorder_tree_walk()const;
    29     RedBlackTreeNode<T>* get_root() const;
    30     ~RedBlackTree();
    31 private:
    32     RedBlackTreeNode<T>* root;
    33     static  RedBlackTreeNode<T> *NIL;
    34     RedBlackTreeNode<T>* get_parent(RedBlackTreeNode<T>* pnode) const;
    35     RedBlackTreeNode<T>* get_left(RedBlackTreeNode<T>* pnode) const;
    36     RedBlackTreeNode<T>* get_right(RedBlackTreeNode<T>* pnode) const;
    37     T get_key(RedBlackTreeNode<T>* pnode) const;
    38     int get_color(RedBlackTreeNode<T>* pnode) const;
    39     void set_color(RedBlackTreeNode<T>* pnode,int color);
    40     void left_rotate(RedBlackTreeNode<T> *pnode);
    41     void right_rotate(RedBlackTreeNode<T> *pnode);
    42     void rb_insert_fixup(RedBlackTreeNode<T> *pnode);
    43     void rb_delete_fixup(RedBlackTreeNode<T> *pnode);
    44     RedBlackTreeNode<T>* get_maxmum(RedBlackTreeNode<T> *root) const;
    45     RedBlackTreeNode<T>* get_minmum(RedBlackTreeNode<T> *root) const;
    46     RedBlackTreeNode<T>* get_successor(RedBlackTreeNode<T> *pnode) const;
    47     RedBlackTreeNode<T>* get_predecessor(RedBlackTreeNode<T> *pnode) const;
    48     RedBlackTreeNode<T>* search_tree_node(const T& k)const;
    49     void make_empty(RedBlackTreeNode<T>* root);
    50 };
    复制代码

    设计过程中采用了C++的模板类型,这样可以支持多种数据类型,使得程序具备扩展性,完整的程序实现如下所示:

      1 #include <iostream>
      2 #include <stack>
      3 using namespace std;
      4 
      5 static const int RED = 0;
      6 static const int BLACK = 1;
      7 
      8 template <class T>
      9 class RedBlackTreeNode
     10 {
     11 public:
     12     RedBlackTreeNode():key(T()),parent(NULL),left(NULL),right(NULL),color(BLACK){}
     13     T key;
     14     RedBlackTreeNode<T>* parent;
     15     RedBlackTreeNode<T>* left;
     16     RedBlackTreeNode<T>* right;
     17     int color;
     18 };
     19 
     20 template <class T>
     21 class RedBlackTree
     22 {
     23 public:
     24     RedBlackTree();
     25     int search_element(const T& k) const;
     26     int get_minmum(T& retmin)const;
     27     int get_maxmum(T& retmax)const;
     28     int get_successor(const T& k,T& ret) const;
     29     int get_predecessor(const T& k,T& ret) const;
     30     int insert_key(const T& k);
     31     int delete_key(const T& k);
     32     void inorder_tree_walk()const;
     33     RedBlackTreeNode<T>* get_root() const;
     34     ~RedBlackTree();
     35 private:
     36     RedBlackTreeNode<T>* root;
     37     static  RedBlackTreeNode<T> *NIL;
     38     RedBlackTreeNode<T>* get_parent(RedBlackTreeNode<T>* pnode) const;
     39     RedBlackTreeNode<T>* get_left(RedBlackTreeNode<T>* pnode) const;
     40     RedBlackTreeNode<T>* get_right(RedBlackTreeNode<T>* pnode) const;
     41     T get_key(RedBlackTreeNode<T>* pnode) const;
     42     int get_color(RedBlackTreeNode<T>* pnode) const;
     43     void set_color(RedBlackTreeNode<T>* pnode,int color);
     44     void left_rotate(RedBlackTreeNode<T> *pnode);
     45     void right_rotate(RedBlackTreeNode<T> *pnode);
     46     void rb_insert_fixup(RedBlackTreeNode<T> *pnode);
     47     void rb_delete_fixup(RedBlackTreeNode<T> *pnode);
     48     RedBlackTreeNode<T>* get_maxmum(RedBlackTreeNode<T> *root) const;
     49     RedBlackTreeNode<T>* get_minmum(RedBlackTreeNode<T> *root) const;
     50     RedBlackTreeNode<T>* get_successor(RedBlackTreeNode<T> *pnode) const;
     51     RedBlackTreeNode<T>* get_predecessor(RedBlackTreeNode<T> *pnode) const;
     52     RedBlackTreeNode<T>* search_tree_node(const T& k)const;
     53     void make_empty(RedBlackTreeNode<T>* root);
     54 };
     55 
     56 template <class T>
     57 RedBlackTreeNode<T>* RedBlackTree<T>::NIL = new RedBlackTreeNode<T>;
     58 
     59 template <class T>
     60 RedBlackTree<T>::RedBlackTree()
     61 {
     62     root = NULL;
     63 }
     64 
     65 template <class T>
     66 int RedBlackTree<T>::search_element(const T& k) const
     67 {
     68     return (NIL != search_tree_node(k));
     69 }
     70 
     71 template <class T>
     72 int RedBlackTree<T>::get_minmum(T& retmin)const
     73 {
     74     if(root)
     75     {
     76         retmin = get_minmum(root)->key;
     77         return 0;
     78     }
     79     return -1;
     80 }
     81 
     82 template <class T>
     83 int RedBlackTree<T>::get_maxmum(T& retmax)const
     84 {
     85     if(root)
     86     {
     87         retmax = get_maxmum(root)->key;
     88         return 0;
     89     }
     90     return -1;
     91 }
     92 
     93 template <class T>
     94 int RedBlackTree<T>::get_successor(const T& k,T& ret) const
     95 {
     96     RedBlackTreeNode<T>* pnode = search_tree_node(k);
     97 
     98     if(pnode != NIL)
     99     {
    100         pnode = get_successor(pnode);
    101         if(pnode != NIL)
    102         {
    103             ret = pnode->key;
    104             return 0;
    105         }
    106         return -1;
    107     }
    108     return -1;
    109 }
    110 template <class T>
    111 int RedBlackTree<T>::get_predecessor(const T& k,T& ret) const
    112 {
    113     RedBlackTreeNode<T>* pnode = search_tree_node(k);
    114     if(pnode != NIL)
    115     {
    116         pnode = get_predecessor(pnode);
    117         if(pnode != NIL)
    118         {
    119             ret = pnode->key;
    120             return 0;
    121         }
    122         return -1;
    123     }
    124     return -1;
    125 }
    126 
    127 template <class T>
    128 int RedBlackTree<T>::insert_key(const T& k)
    129 {
    130     RedBlackTreeNode<T> *newnode = new RedBlackTreeNode<T>;
    131     newnode->key = k;
    132     newnode->color = RED;
    133     newnode->left = NIL;
    134     newnode->right = NIL;
    135     newnode->parent = NIL;
    136 
    137     if(NULL == root)
    138         root = newnode;
    139     else
    140     {
    141         RedBlackTreeNode<T>* pnode = root;
    142         RedBlackTreeNode<T>* qnode;
    143         while(pnode != NIL)
    144         {
    145             qnode = pnode;
    146             if(pnode->key > newnode->key)
    147                 pnode = pnode->left;
    148             else
    149                 pnode = pnode->right;
    150         }
    151         newnode->parent = qnode;
    152         if(qnode->key > newnode->key)
    153             qnode->left = newnode;
    154         else
    155             qnode->right = newnode;
    156     }
    157     rb_insert_fixup(newnode);
    158     return 0;
    159 }
    160 
    161 template <class T>
    162 int RedBlackTree<T>::delete_key(const T& k)
    163 {
    164     RedBlackTreeNode<T>* pnode = search_tree_node(k);
    165     if(NIL != pnode)
    166     {
    167         RedBlackTreeNode<T>* qnode,*tnode;
    168         if(get_left(pnode) == NIL || get_right(pnode) == NIL)
    169             qnode = pnode;
    170         else
    171             qnode = get_successor(pnode);
    172         if(get_left(qnode) != NIL)
    173             tnode = get_left(qnode);
    174         else
    175             tnode = get_right(qnode);
    176         tnode->parent = get_parent(qnode);
    177         if(get_parent(qnode) == NIL)
    178             root = tnode;
    179         else if(qnode == get_left(get_parent(qnode)))
    180             qnode->parent->left = tnode;
    181         else
    182             qnode->parent->right = tnode;
    183         if(qnode != pnode)
    184              pnode->key = get_key(qnode);
    185         if(get_color(qnode) == BLACK)
    186             rb_delete_fixup(tnode);
    187         delete qnode;
    188         return 0;
    189     }
    190     return -1;
    191 }
    192 
    193 template <class T>
    194 RedBlackTree<T>::~RedBlackTree()
    195 {
    196     make_empty(root);
    197 }
    198 template <class T>
    199 RedBlackTreeNode<T>* RedBlackTree<T>:: get_root() const
    200 {
    201     return root;
    202 }
    203 template <class T>
    204 RedBlackTreeNode<T>* RedBlackTree<T>::get_parent(RedBlackTreeNode<T>* pnode) const
    205 {
    206     return pnode->parent;
    207 }
    208 template <class T>
    209 RedBlackTreeNode<T>* RedBlackTree<T>::get_left(RedBlackTreeNode<T>* pnode) const
    210 {
    211     return pnode->left;
    212 }
    213 template <class T>
    214 RedBlackTreeNode<T>* RedBlackTree<T>::get_right(RedBlackTreeNode<T>* pnode) const
    215 {
    216     return pnode->right;
    217 }
    218 template <class T>
    219 T RedBlackTree<T>::get_key(RedBlackTreeNode<T>* pnode) const
    220 {
    221     return pnode->key;
    222 }
    223 template <class T>
    224 int RedBlackTree<T>::get_color(RedBlackTreeNode<T>* pnode) const
    225 {
    226     return pnode->color;
    227 }
    228 
    229 template <class T>
    230 void RedBlackTree<T>::set_color(RedBlackTreeNode<T>* pnode,int color)
    231 {
    232     pnode->color = color;
    233 }
    234 
    235 template <class T>
    236 void RedBlackTree<T>::left_rotate(RedBlackTreeNode<T> *pnode)
    237 {
    238     RedBlackTreeNode<T>* rightnode = pnode->right;
    239     pnode->right = rightnode->left;
    240     if(rightnode->left != NIL)
    241         rightnode->left->parent = pnode;
    242     rightnode->parent = pnode->parent;
    243     if(pnode->parent == NIL)
    244         root = rightnode;
    245     else if(pnode == pnode->parent->left)
    246         pnode->parent->left = rightnode;
    247     else
    248         pnode->parent->right = rightnode;
    249     rightnode->left = pnode;
    250     pnode->parent = rightnode;
    251 }
    252 
    253 template <class T>
    254 void RedBlackTree<T>::right_rotate(RedBlackTreeNode<T> *pnode)
    255 {
    256     RedBlackTreeNode<T>* leftnode = pnode->left;
    257     pnode->left = leftnode->right;
    258     if(leftnode->right != NIL)
    259         leftnode->right->parent = pnode;
    260     leftnode->parent = pnode->parent;
    261     if(pnode->parent == NIL)
    262         root = leftnode;
    263     else if(pnode == pnode->parent->left)
    264         pnode->parent->left = leftnode;
    265     else
    266         pnode->parent->right = leftnode;
    267     leftnode->right = pnode;
    268     pnode->parent = leftnode;
    269 }
    270 template <class T>
    271 void RedBlackTree<T>::rb_insert_fixup(RedBlackTreeNode<T>*pnode)
    272 {
    273     RedBlackTreeNode<T> *qnode,*tnode;
    274     //当pnode的父节点为红色时,破坏性质4
    275     while(get_color(get_parent(pnode))== RED)
    276     {
    277         qnode = get_parent(get_parent(pnode));//祖父结点
    278         if(get_parent(pnode) == get_left(qnode))
    279         {
    280             tnode = get_right(qnode);//pnode的叔叔结点
    281             if(get_color(tnode) == RED) //case1 叔叔结点为红色
    282             {
    283                 set_color(get_parent(pnode),BLACK);
    284                 set_color(tnode,BLACK);
    285                 set_color(qnode,RED);
    286                 pnode = qnode;
    287             }
    288             else  //case 2 or case 3
    289             {
    290                 if(pnode == get_right(get_parent(pnode)))  //case2 pnode为右孩子
    291                 {
    292                     pnode = get_parent(pnode);
    293                     left_rotate(pnode);
    294                 }
    295                 //case3 pnode为左孩子
    296                 set_color(get_parent(pnode),BLACK);
    297                 qnode = get_parent(get_parent(pnode));
    298                 set_color(qnode,RED);
    299                 right_rotate(qnode);
    300             }
    301         }
    302         else
    303         {
    304             tnode = get_left(qnode);
    305             if(get_color(tnode) == RED)
    306             {
    307                 set_color(get_parent(pnode),BLACK);
    308                 set_color(tnode,BLACK);
    309                 set_color(qnode,RED);
    310                 pnode = qnode;
    311             }
    312             else
    313             {
    314                 if(pnode == get_left(get_parent(pnode)))
    315                 {
    316                     pnode = get_parent(pnode);
    317                     right_rotate(pnode);
    318                 }
    319                 set_color(get_parent(pnode),BLACK);
    320                 qnode = get_parent(get_parent(pnode));
    321                 set_color(qnode,RED);
    322                 left_rotate(qnode);
    323             }
    324         }
    325     }
    326     set_color(root,BLACK);
    327 }
    328 
    329 template <class T>
    330 void RedBlackTree<T>::rb_delete_fixup(RedBlackTreeNode<T> *pnode)
    331 {
    332     while(pnode != root && get_color(pnode) == BLACK)
    333     {
    334         RedBlackTreeNode<T> *qnode,*tnode;
    335         if(pnode == get_left(get_parent(pnode)))
    336         {
    337             qnode = get_right(get_parent(pnode));
    338             if(get_color(qnode) == RED)
    339             {
    340                 set_color(qnode,BLACK);
    341                 set_color(get_parent(pnode),RED);
    342                 left_rotate(get_parent(pnode));
    343                 qnode = get_right(get_parent(pnode));
    344             }
    345             if(get_color(get_left(qnode)) == BLACK && get_color(get_right(qnode)) == BLACK)
    346             {
    347                 set_color(qnode,RED);
    348                 pnode = get_parent(pnode);
    349             }
    350             else
    351             {
    352                 if(get_color(get_right(qnode)) == BLACK)
    353                 {
    354                     set_color(get_left(qnode),BLACK);
    355                     set_color(qnode,RED);
    356                     right_rotate(qnode);
    357                     qnode = get_right(get_parent(pnode));
    358                 }
    359                 set_color(qnode,get_color(get_parent(pnode)));
    360                 set_color(get_parent(pnode),BLACK);
    361                 set_color(get_right(qnode),BLACK);
    362                 left_rotate(get_parent(pnode));
    363                 pnode = root;
    364             }
    365         }
    366         else
    367         {
    368             qnode = get_left(get_parent(pnode));
    369             if(get_color(qnode) == RED)
    370             {
    371                 set_color(qnode,BLACK);
    372                 set_color(get_parent(pnode),RED);
    373                 right_rotate(get_parent(pnode));
    374                 qnode = get_left(get_parent(pnode));
    375             }
    376             if(get_color(get_right(qnode)) == BLACK && get_color(get_left(qnode)) == BLACK)
    377             {
    378                 set_color(qnode,RED);
    379                 pnode = get_parent(pnode);
    380             }
    381             else
    382             {
    383                 if(get_color(get_left(qnode)) == BLACK)
    384                 {
    385                     set_color(get_right(qnode),BLACK);
    386                     set_color(qnode,RED);
    387                     left_rotate(qnode);
    388                     qnode = get_left(get_parent(pnode));
    389                 }
    390                 set_color(qnode,get_color(get_parent(pnode)));
    391                 set_color(get_parent(pnode),BLACK);
    392                 set_color(get_left(qnode),BLACK);
    393                 right_rotate(get_parent(pnode));
    394                 pnode = root;
    395             }
    396         }
    397     }
    398     set_color(pnode,BLACK);
    399 }
    400 
    401 template <class T>
    402 RedBlackTreeNode<T>* RedBlackTree<T>::get_maxmum(RedBlackTreeNode<T> *root) const
    403 {
    404     RedBlackTreeNode<T> *pnode = root;
    405     while(pnode->right != NIL)
    406         pnode = pnode->right;
    407     return pnode;
    408 }
    409 
    410 template <class T>
    411 RedBlackTreeNode<T>* RedBlackTree<T>::get_minmum(RedBlackTreeNode<T> *root) const
    412 {
    413     RedBlackTreeNode<T> *pnode = root;
    414     while(pnode->left != NIL)
    415         pnode = pnode->left;
    416     return pnode;
    417 }
    418 
    419 template <class T>
    420 RedBlackTreeNode<T>* RedBlackTree<T>:: get_successor(RedBlackTreeNode<T> *pnode) const
    421 {
    422     if(pnode->right != NIL)
    423         return get_minmum(pnode->right);
    424     RedBlackTreeNode<T>* parentnode = get_parent(pnode);
    425     while(parentnode != NIL && get_right(parentnode) == pnode)
    426     {
    427             pnode = parentnode;
    428             parentnode = get_parent(pnode);
    429     }
    430     return parentnode;
    431 }
    432 
    433 template <class T>
    434 RedBlackTreeNode<T>* RedBlackTree<T>::get_predecessor(RedBlackTreeNode<T> *pnode) const
    435 {
    436     if(pnode->left != NIL)
    437         return get_maxmum(pnode->left);
    438     RedBlackTreeNode<T>* parentnode = get_parent(pnode);
    439     while(parentnode != NIL && get_left(parentnode) == pnode)
    440     {
    441         pnode = parentnode;
    442         parentnode = get_parent(pnode);
    443     }
    444     return parentnode;
    445 }
    446 
    447 template <class T>
    448 RedBlackTreeNode<T>* RedBlackTree<T>:: search_tree_node(const T& k)const
    449 {
    450     RedBlackTreeNode<T>* pnode = root;
    451     while(pnode != NIL)
    452     {
    453         if(pnode->key == k)
    454             break;
    455         else if(pnode->key > k)
    456             pnode = pnode->left;
    457         else
    458             pnode = pnode->right;
    459     }
    460     return pnode;
    461 }
    462 
    463 template <class T>
    464 void RedBlackTree<T>::make_empty(RedBlackTreeNode<T>* root)
    465 {
    466     if(root)
    467     {
    468         RedBlackTreeNode<T> *pleft = root->left;
    469         RedBlackTreeNode<T>* pright = root->right;
    470         delete root;
    471         if(pleft != NIL)
    472             make_empty(pleft);
    473         if(pright != NIL)
    474             make_empty(pright);
    475     }
    476 }
    477 template <class T>
    478 void RedBlackTree<T>::inorder_tree_walk()const
    479 {
    480     if(NULL != root)
    481      {
    482          stack<RedBlackTreeNode<T>* > s;
    483          RedBlackTreeNode<T> *ptmpnode;
    484          ptmpnode = root;
    485          while(NIL != ptmpnode || !s.empty())
    486          {
    487              if(NIL != ptmpnode)
    488              {
    489                  s.push(ptmpnode);
    490                  ptmpnode = ptmpnode->left;
    491              }
    492              else
    493              {
    494                  ptmpnode = s.top();
    495                  s.pop();
    496                  cout<<ptmpnode->key<<":";
    497                  if(ptmpnode->color == BLACK)
    498                     cout<<"Black"<<endl;
    499                  else
    500                     cout<<"Red"<<endl;
    501                  ptmpnode = ptmpnode->right;
    502              }
    503          }
    504      }
    505 }
    506 int main()
    507 {
    508     RedBlackTree<int> rbtree;
    509     int value;
    510     rbtree.insert_key(41);
    511     rbtree.insert_key(38);
    512     rbtree.insert_key(31);
    513     rbtree.insert_key(12);
    514     rbtree.insert_key(19);
    515     rbtree.insert_key(8);
    516     cout<<"root is: "<<rbtree.get_root()->key<<endl;
    517     cout<<"Inorder walk red black tree:"<<endl;
    518     rbtree.inorder_tree_walk();
    519     if(rbtree.get_minmum(value) == 0)
    520         cout<<"minmum is: "<<value<<endl;
    521     if(rbtree.get_maxmum(value) == 0)
    522         cout<<"maxmum is: "<<value<<endl;
    523     if(rbtree.get_successor(19,value) == 0)
    524         cout<<"19 successor is: "<<value<<endl;
    525     if(rbtree.get_predecessor(19,value) == 0)
    526         cout<<"19 predecessor is: "<<value<<endl;
    527     if(rbtree.delete_key(38)==0)
    528         cout<<"delete 38 successfully"<<endl;
    529      cout<<"root is: "<<rbtree.get_root()->key<<endl;
    530     cout<<"Inorder walk red black tree:"<<endl;
    531     rbtree.inorder_tree_walk();
    532     return 0;
    533 }

    程序测试结果如下所示:


      实现过程中感触非常多,需要很大的耐心去调试程序,关键的是insert和delete操作。

  • 相关阅读:
    植物大战僵尸英文原版
    2020-5-13递归练习 六人参加竞赛
    2020-5-1递归练习
    java当脚本用。QQ表白轰炸机
    面向实战---VUE项目的文件加载顺序,或者加载流程,以及index.html,main.js,app.vue的区别
    CSS多个view随机分布,不重叠,如何实现呢?
    vite项目才踩坑日志1
    css属性之clip-path
    纯CSS3实现的阳光海鸥沙滩遮阳伞和比基尼美女风景动画效果源码
    TP5 中使用wherein 进行查询,太慢了,怎么优化?
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8462035.html
Copyright © 2011-2022 走看看