算法导论--动态顺序统计与区间树

本文的基础是红黑树 算法导论–红黑树

通过在基础的数据结构中添加一些附加信息,来扩张一种标准的数据结构,然后编写新的操作来支持所需要的应用。下面是介绍在红黑树的基础上扩张的数据结构。

1.动态顺序统计

动态顺序统计可以在O(lgn)时间内确定任何的顺序统计量(即在n个元素的集合中,能在O(lgn)的时间内确定第i小的元素),同时也可以在O(lgn)的时间内计算一个元素的秩(即它在中序遍历下的位置顺序)。

1 添加附加信息

结点x中加入x.size , size的大小为以x为根的子树(包含x本身)的内结数,即子树的大小。我们定义哨兵的size为0,如下图:

结点内,虚线上方为关键字key,下方为结点的size。

可以看出: x.size = x.left.size + x.right.size +1;

enum colors{red,black};//枚举类型
typedef struct Node
{
   struct Node * p;
   struct Node *left;
   struct Node *right;
   int key;
   enum  colors color;  //颜色属性
   int size;           //新添加的属性size
}Node;

2 修改基本操作

插入操作:

为了对子树规模的维护,需要在不影响插入和删除操作的渐进运行时间的前提下,修改基本的操作。

第一阶段:新节点的插入过程中,需要对从根到将要插入的位置过程中遍历的结点的size加1;

第二阶段:维护红黑树的左旋和右旋函数需要在最后添加以下语句:

 /*左旋*/
    y.size = x.size;
    x.size = x.left.size + x.right.size +1;
 /*右旋*/
    x.size = y.size;
    y.size = y.left.size + y.right.size +1;

删除操作:

第一阶段:如果要删除的结点z少于两个孩子,则从z到根T的过程遍历的结点size减1;如果要删除的结点z多于两个孩子,则从z的后继y处向上到T的过程中,遍历的结点size减1;

第二阶段:

也是同样在左右旋过程中,添加以上的语句;

插入操作和删除操作运行时间都是O(lgn).

3 设计新的操作

1.给定秩的元素的检索

调用函数OS_Select(Node * x,int i)检索出在以x为根的子树中,第i小的关键字的结点。运行时间为O(lgn);

Node * OS_Select(Node *x,int i)
{
    int r =x->left->size+1;           //计算以结点x为根的子树中顺序统计量r
    if (i == r)
        return x;
    else if (i < r)
        return OS_Select(x->left,i);  //在x的左子树里继续递归查找
    else
        return OS_Select(x->right,i-r);//在x的右子树里继续递归查找
}

2.确定一个元素的秩

给定指向T中,结点x的指针,过程OS_Rank返回对T中序遍历对应的线性序中x的位置;运行时间为O(lgn)

int OS_Rank(Node *T,Node * x)
{
    int r =x->left->size+1;  //计算以结点x为根的子树中顺序统计量r
    Node * y =x;
    while(y != T)                 //叠加到root根节点位置
    {
        if (y == y->p->right)          //父节点的右子树输出在左子树和根之后,顺序统计量叠加
        {
            r=r+y->p->left->size+1;
        }
        y = y->p;                      //若属于左子树,直接跳向上层
    }
    return r;
}

4 完整代码

/*
CSDN 勿在浮砂筑高台
http://blog.csdn.net/luoshixian099
算法导论--顺序统计量
2015年5月20日
*/
#include <STDIO.H>
#include <STDLIB.H>
enum colors{red,black};//枚举类型
typedef struct Node
{
   struct Node * p;
   struct Node *left;
   struct Node *right;
   int key;
   enum  colors color;
   int size;                       //添加附加信息size
}Node;
Node *T_NIL=NULL;                 //建立全部变量 T_NIL

Node * Tree_Minimum(Node * T)     //找最小结点
{
    while(T->left != T_NIL)
        T=T->left;
    return T;
}
void Inorder_Tree_Walk(Node * T)  //中序遍历树T,输出
{
    if ( T != T_NIL)
    {
        Inorder_Tree_Walk(T->left);   //递归其左孩子
        printf("%d",T->key);        //输出根的关键字
        if (T->color == 0)
        {
            printf("-R");
        }
        else
        {
            printf("-B");
        }
        printf("-(%d)  ",T->size);
        Inorder_Tree_Walk(T->right);  //递归其右孩子
    }
}
void Pre_Tree_Walk(Node * T)  //
{
    if ( T != T_NIL)
    {
        printf("%d    ",T->key);          //输出根的关键字
        Pre_Tree_Walk(T->left);   //递归其左孩子
        Pre_Tree_Walk(T->right);  //递归其右孩子

    }
}

void Left_Rotate(Node **T,Node * x)   //左旋
{
   Node *y=x->right;

   x->right =y->left;
   if (y->left != T_NIL)
       y->left->p=x;
   y->p=x->p;
   if(x->p==T_NIL)
      *T=y;
   else if (x == x->p->left)
      x->p->left = y;
   else
      x->p->right = y;
   y->left = x;
   x->p=y;

   y->size = x->size;                         //添加语句维护size
   x->size = x->left->size+x->right->size+1;
}
void Right_Rotate(Node **T,Node * y)   //右旋
{
    Node *x=y->left;

    y->left =x->right;
    if (x->right != T_NIL)
        x->right->p=y;
    x->p=y->p;
    if(y->p==T_NIL)
        *T=x;
    else if (y == y->p->left)
        y->p->left = x;
    else
        y->p->right = x;
    x->right = y;
    y->p=x;

   x->size = y->size;                     //添加语句维护size
   y->size = y->left->size+y->right->size+1;
}
Node* RB_Insert_Fixup(Node *T,Node *z)
{
    Node * y=NULL;
   while( z->p->color == red)       //违反了性质4,迭代进行修正
   {
      if (z->p == z->p->p->left)
      {
          y = z->p->p->right;
          if ( y->color == red)    // case 1 叔结点为红色
          {
             z->p->color = black;    //父节点涂黑
             y->color = black;       //叔结点涂黑
             z->p->p->color = red;   //祖结点涂红
             z = z->p->p;            //向上迭代,更新z的位置
          }
          else if ( z == z->p->right) //case 2 叔结点为黑色且z为双亲的右孩子
          {
             z = z->p;
             Left_Rotate(&T,z);
             z->p->color = black;    //case2 已转为 case3 继续处理
             z->p->p->color = red;
             Right_Rotate(&T,z->p->p);// while循环终止
          }
          else                      // case 3 叔结点为黑色且z为双亲的左孩子
          {
             z->p->color = black;
             z->p->p->color = red;
             Right_Rotate(&T,z->p->p);//   while循环终止
          }
      }

      else                      //对称处理
      {

          y = z->p->p->left;
          if ( y->color == red)    // case 1 叔结点为红色
          {
              z->p->color = black;
              y->color = black;
              z->p->p->color = red;
              z = z->p->p;
          }

          else if ( z == z->p->left) //case 2 叔结点为黑色且z为双亲的右孩子
          {
              z = z->p;
              Right_Rotate(&T,z);
              z->p->color = black;
              z->p->p->color = red;
              Left_Rotate(&T,z->p->p);//
          }
          else                      // case 3
          {
              z->p->color = black;
              z->p->p->color = red;
              Left_Rotate(&T,z->p->p);
          }

      }
   }

   T->color = black;          //保证不会违反性质2,对根节点涂黑
   return T;
}
Node *RB_Insert(Node *Root,Node * z)   //红黑树插入,返回树的根
{
    Node * y=T_NIL;
    Node * x=Root;
    while( x != T_NIL)                 //找到结点z要插入的位置
    {
        x->size+=1;                    //插入过程中,遍历的结点size加1

        y=x;
        if (z->key < x->key)
            x = x->left;
        else
            x = x->right;
    }
    z->p = y;
    if ( y == T_NIL)    //插入第一个结点作为根节点的情况
        Root = z;
    else if (z->key < y->key)
        y->left = z;
    else
        y->right = z;

    Root = RB_Insert_Fixup(Root,z);    //插入完毕后,对红黑树的颜色进行修正
    return Root;
}
Node * Establish(int *A,int len)   //建立红黑树
{
   Node * T,*node;
   int i=0;
   node=NULL;
   T_NIL=(Node *)malloc(sizeof(Node));  //建立T_NIL结点
   T_NIL->p=NULL;
   T_NIL->left=NULL;
   T_NIL->right=NULL;
   T_NIL->key=-1;
   T_NIL->color=black;
   T_NIL->size=0;
   T=T_NIL;
   for (i=0;i<len;i++)
   {
       node =(Node *)malloc(sizeof(Node));
       node->p =T_NIL;
       node->left=T_NIL;
       node->right=T_NIL;
       node->key=A[i];
       node->color=red;
       node->size=1;
       T=RB_Insert(T,node);
   }
   return T;
}

void RB_Transplant(Node **T,Node * u,Node * v)  //结点替代函数
{
  if (u->p == T_NIL)
      *T = v;
  else if (u == u->p->left)
     u->p->left = v;
  else
     u->p->right = v;
  v->p = u->p;               //此处赋值无条件,v如果是T_NIL也要进行赋值
}
void RB_Delete_Fixup(Node * T,Node * x)
{
    Node *w=NULL;
  while( x != T && x->color == black)      //循环迭代处理
  {
      if ( x == x->p->left )
      {
        w = x->p->right;
        if (w->color == red)             // case 1 ------> case 2 , case 3 ,case 4
        {
            w->color = black;
            x->p->color =red;
            Left_Rotate(&T,x->p);
            w = x->p->right;
        }
        if ( w->left->color == black && w->right->color == black ) //case 2 ------>go on / stop
        {
            w->color = red;
            x = x->p;
        }
        else if ( w->right->color == black)   // case 3 ---->case 4---->stop
        {
           w->left->color = black;
           w->color =red ;
           Right_Rotate(&T,w);

           w = x->p->right ;                   //转成case 4处理
           w->color = x->p->color;
           x->p->color = black;
           w->right->color = black;
           Left_Rotate(&T,x->p);
           x = T;
        }
        else                               // case 4 ------------------->stop
        {
            w->color = x->p->color;
            x->p->color = black;
            w->right->color = black;
            Left_Rotate(&T,x->p);
            x = T;
        }
      }
      else
      {
          w = x->p->left;
          if (w->color == red)                // case 1 ------> case 2 , case 3 ,case 4
          {
              w->color = black;
              x->p->color =red;
              Right_Rotate(&T,x->p);
              w = x->p->left;
          }
          if ( w->right->color == black && w->left->color == black ) //case 2 ------>go on/stop
          {
              w->color = red;
              x = x->p;
          }
          else if ( w->left->color == black)      // case 3 -----> case 4----->stop
          {
              w->right->color = black;
              w->color =red ;
              Left_Rotate(&T,w);

              w = x->p->left ;                    //转成case 4处理
              w->color = x->p->color;
              x->p->color = black;
              w->left->color = black;
              Right_Rotate(&T,x->p);
              x = T;
          }
          else                                  // case 4 -------------->stop
          {
              w->color = x->p->color;
              x->p->color = black;
              w->left->color = black;
              Right_Rotate(&T,x->p);
              x = T;
        }
      }
  }

  x->color = black;                       //可能由case2退出,那把x涂黑即可,见分析!也可能有case4退出,把根节点涂黑
}
Node * RB_Delete(Node *T ,Node *z)
{

    Node * x =NULL;
    Node * y =z;
    Node *temp=y->p;
    enum colors y_original_color = y->color;   //记录下删除前z的颜色
    if ( z->left == T_NIL)                     //左子树不存在的情况
    {
        x = z->right;
        RB_Transplant(&T,z,z->right);
    }
    else if ( z->right == T_NIL)              //右子树不存在
    {
       x = z->left;
       RB_Transplant(&T,z,z->left);
    }
    else                                     //左右都存在的情况
    {
       y = Tree_Minimum(z->right);            //找到后继y
       temp=y->p;
       y_original_color = y->color;           //记录下y转移前的颜色
       x = y->right;
       if ( y->p == z)                       //如果y是z的子结点
       {
         x->p = y;
       }
       else
       {
           RB_Transplant(&T,y,y->right);    //如果y不是z的子结点,用y的右子树代替y的位置
           y->right = z->right;
           y->right->p = y;
       }
       RB_Transplant(&T,z,y);           //y替代z的位置 ,不论y是不是T_NIL
       y->left = z->left;
       y->left->p = y;
       y->color = z->color;             //把y的颜色改成z的颜色

       y->size =y->left->size+y->right->size+1;
    }

     while(temp != T_NIL)             //从删除的位置或后继的位置向上遍历size--,直到根节点为止
        {
            temp->size--;
            temp = temp->p;
        }

    if ( y_original_color == black)   //判断y的颜色,若为黑色,需要修复
        RB_Delete_Fixup(T,x);
    return T;
}

Node * Tree_Search(Node *T ,int k)  //寻找数k是否在树中,且返回数k的地址
{   

    while(T !=T_NIL && T->key != k)
    {
        if ( k < T->key)
            T=T->left;
        else
            T=T->right;
    }

    if ( T == T_NIL)
    {
        return NULL;
    }

    else
    {
        return T;
    }

}
Node * OS_Select(Node *x,int i)        //确定以x为根节点的子树,第i小的关键字
{
    int r =x->left->size+1;
    if (i == r)
        return x;
    else if (i < r)
        return OS_Select(x->left,i);
    else
        return OS_Select(x->right,i-r);
}
int OS_Rank(Node *T,Node * x)         //确定x在树T中序遍历中的位置顺序
{
    int r =x->left->size+1;
    Node * y =x;
    while(y != T)
    {
        if (y == y->p->right)
        {
            r=r+y->p->left->size+1;
        }
        y = y->p;
    }
    return r;
}
void main()
{
    int A[]={2,5,1,6,3,8,4,9,7};

    int length = sizeof(A)/sizeof(A[0]); //数组A的长度
    Node *T =Establish(A,length);        //建立红黑树,返回根节点T

    printf("中序遍历:\n");
    Inorder_Tree_Walk(T);printf("\n");   //中序遍历输出

    printf("先序遍历:\n");              //先序遍历输出
    Pre_Tree_Walk(T);printf("\n");

    printf("__%d__\n",OS_Select(T,5)->key);
    printf("--%d--\n",OS_Rank(T,Tree_Search(T,3)));

    printf("-----------删除操作后-------------\n");

    T=RB_Delete(T,Tree_Search(T,2));
    T=RB_Delete(T,Tree_Search(T,5));
    T=RB_Delete(T,Tree_Search(T,7));
    T=RB_Delete(T,Tree_Search(T,4));
    printf("中序遍历:\n");
    Inorder_Tree_Walk(T);
    printf("\n");

    printf("先序遍历:\n");
    Pre_Tree_Walk(T);

    printf("\n");
}

2.区间树

区间树是通过扩张红黑树来构成由区间构成的动态集合。结点的属性由一个关键字key变成了一个区间。

1.添加附加信息

添加区间信息INT,INT结构包含区间的左右端点。还包含Max属性,它是以自身为根的子树中所有的区间的端点最大值。(上图中虚线下方)

enum colors{red,black};//枚举类型
struct Interval //区间
{
    int low;
    int high;
};
typedef struct Node
{
   struct Node * p;
   struct Node *left;
   struct Node *right;
   enum  colors color;
   //添加的属性
   struct Interval INT;   //存储结点区间信息
   int  Max;              //以结点为根的所有区间端点的最大值
}Node;

2.修改基本操作

修改红黑树的插入和删除操作维添加的信息,都能保证在O(lgn)的时间内完成;

1.插入操作

第一步:由于区间树采用区间左端点作为关键字进行插入,遍历时通过比较INT.low的方式插入;插入前令Max=high,插入时从根节点开始遍历到要插入的位置,把遍历的结点的Max与新添加的结点z的Max进行比较,如果z.Max>x.Max ,更新结点的x.Max=z.Max

Node *Interval_Insert(Node *Root,Node * z)   //红黑树插入,返回树的根
{
    Node * y=T_NIL;
    Node * x=Root;
    while( x != T_NIL)                 //找到结点z要插入的位置
    {
        if ( z->Max > x->Max)     //比较新插入的结点z与结点x的Max大小;
        {
            x->Max = z->Max;
        }
        y=x;
        if (z->INT.low < x->INT.low)
            x = x->left;
        else
            x = x->right;
    }
    z->p = y;
    if ( y == T_NIL)    //插入第一个结点作为根节点的情况
        Root = z;
    else if (z->INT.low < y->INT.low)
        y->left = z;
    else
        y->right = z;

    Root = Interval_Insert_Fixup(Root,z);    //插入完毕后,对红黑树的颜色进行修正
    return Root;
}

第二步:由于左旋右旋会破坏区间的性质,在函数代码后添加更新信息

void Left_Rotate(Node **T,Node * x)   //左旋
{
   Node *y=x->right;

   x->right =y->left;
   if (y->left != T_NIL)
       y->left->p=x;
   y->p=x->p;
   if(x->p==T_NIL)
      *T=y;
   else if (x == x->p->left)
      x->p->left = y;
   else
      x->p->right = y;
   y->left = x;
   x->p=y;

  y->Max = x->Max;                         //添加语句维护Max
  x->Max = GetMax(x->left->Max,x->right->Max,x->INT.high);
}
void Right_Rotate(Node **T,Node * y)   //右旋
{
    Node *x=y->left;

    y->left =x->right;
    if (x->right != T_NIL)
        x->right->p=y;
    x->p=y->p;
    if(y->p==T_NIL)
        *T=x;
    else if (y == y->p->left)
        y->p->left = x;
    else
        y->p->right = x;
    x->right = y;
    y->p=x;

  x->Max = y->Max;                         //添加语句维护Max
  y->Max = GetMax(y->left->Max,y->right->Max,y->INT.high);
}

2.删除操作

第一步:被删除的结点z可能会影响整个区间树的性质,如果结点z少于两个孩子,则沿着z上升到根节点为止,对树重新更新维护;如果有两个孩子则从后继出发,进行维护;

while( temp != T_NIL )             //从要删除的结点或其后继开始向上修复区间树
    {
            temp->Max = GetMax(temp->left->Max,temp->right->Max,temp->INT.high);
            temp = temp->p;     //每次一层,至多lgn层
    }

第二步:同上,也是在左旋右旋函数后添加代码。

3.设计新的操作

判断给定的一个区间i位于区间树的哪个位置。区间之间的关系:

a重叠的情况;b、c不重叠的情况

不重叠的情况用代码表示为

x->INT.high < i->low
x->INT.low  > i->high

如果存在区间与i重叠则返回结点的位置,否则返回T_NIL

Node * Interval_Search(Node *T ,struct Interval *i)  //寻找数k是否在树中,且返回数k的地址
{
    Node * x = T;
    while(x != T_NIL && ((x->INT.high < i->low)||(x->INT.low > i->high)))  //不重叠
    {
        if (x->left != T_NIL && x->left->Max >= i->low)  //在其左子树中搜索
        {
            x = x->left;
        }
        else
        {
            x = x->right;
        }
    }

    return x;
}

每次迭代都是一层,至多lgn层;所以耗时O(lgn)的时间

4.完整代码

/*
CSDN 勿在浮砂筑高台
http://blog.csdn.net/luoshixian099
算法导论--区间树
2015年5月20日
*/
#include <STDIO.H>
#include <STDLIB.H>
enum colors{red,black};//枚举类型
struct Interval //区间
{
    int low;
    int high;
};
typedef struct Node
{
   struct Node * p;
   struct Node *left;
   struct Node *right;
   enum  colors color;
   //添加的属性
   struct Interval INT;   //存储结点区间信息
   int  Max;              //以结点为根的所有区间端点的最大值
}Node;
Node *T_NIL=NULL;                 //建立全部变量 T_NIL

int GetMax(int a,int b,int c)    //返回a,b,c最大值
{
    return a>b?(a>c?a:c):(b>c?b:c);
}
Node * Tree_Minimum(Node * T)     //找最小结点
{
    while(T->left != T_NIL)
        T=T->left;
    return T;
}
void Inorder_Tree_Walk(Node * T)  //中序遍历树T,输出
{
    if ( T != T_NIL)
    {
        Inorder_Tree_Walk(T->left);   //递归其左孩子
        printf("%d",T->INT.low);        //输出根的关键字
        if (T->color == 0)
        {
            printf("-R(%d)  ",T->Max);
        }
        else
        {
            printf("-B(%d)   ",T->Max);
        }
        Inorder_Tree_Walk(T->right);  //递归其右孩子
    }
}
void Pre_Tree_Walk(Node * T)  //
{
    if ( T != T_NIL)
    {
        printf("%d    ",T->INT.low);          //输出根的关键字
        Pre_Tree_Walk(T->left);   //递归其左孩子
        Pre_Tree_Walk(T->right);  //递归其右孩子

    }
}

void Left_Rotate(Node **T,Node * x)   //左旋
{
   Node *y=x->right;

   x->right =y->left;
   if (y->left != T_NIL)
       y->left->p=x;
   y->p=x->p;
   if(x->p==T_NIL)
      *T=y;
   else if (x == x->p->left)
      x->p->left = y;
   else
      x->p->right = y;
   y->left = x;
   x->p=y;

  y->Max = x->Max;                         //添加语句维护Max
  x->Max = GetMax(x->left->Max,x->right->Max,x->INT.high);
}
void Right_Rotate(Node **T,Node * y)   //右旋
{
    Node *x=y->left;

    y->left =x->right;
    if (x->right != T_NIL)
        x->right->p=y;
    x->p=y->p;
    if(y->p==T_NIL)
        *T=x;
    else if (y == y->p->left)
        y->p->left = x;
    else
        y->p->right = x;
    x->right = y;
    y->p=x;

  x->Max = y->Max;                         //添加语句维护Max
  y->Max = GetMax(y->left->Max,y->right->Max,y->INT.high);
}
Node* Interval_Insert_Fixup(Node *T,Node *z)
{
    Node * y=NULL;
   while( z->p->color == red)       //违反了性质4,迭代进行修正
   {
      if (z->p == z->p->p->left)
      {
          y = z->p->p->right;
          if ( y->color == red)    // case 1 叔结点为红色
          {
             z->p->color = black;    //父节点涂黑
             y->color = black;       //叔结点涂黑
             z->p->p->color = red;   //祖结点涂红
             z = z->p->p;            //向上迭代,更新z的位置
          }
          else if ( z == z->p->right) //case 2 叔结点为黑色且z为双亲的右孩子
          {
             z = z->p;
             Left_Rotate(&T,z);
             z->p->color = black;    //case2 已转为 case3 继续处理
             z->p->p->color = red;
             Right_Rotate(&T,z->p->p);// while循环终止
          }
          else                      // case 3 叔结点为黑色且z为双亲的左孩子
          {
             z->p->color = black;
             z->p->p->color = red;
             Right_Rotate(&T,z->p->p);//   while循环终止
          }
      }

      else                      //对称处理
      {

          y = z->p->p->left;
          if ( y->color == red)    // case 1 叔结点为红色
          {
              z->p->color = black;
              y->color = black;
              z->p->p->color = red;
              z = z->p->p;
          }

          else if ( z == z->p->left) //case 2 叔结点为黑色且z为双亲的右孩子
          {
              z = z->p;
              Right_Rotate(&T,z);
              z->p->color = black;
              z->p->p->color = red;
              Left_Rotate(&T,z->p->p);//
          }
          else                      // case 3
          {
              z->p->color = black;
              z->p->p->color = red;
              Left_Rotate(&T,z->p->p);
          }

      }
   }

   T->color = black;          //保证不会违反性质2,对根节点涂黑
   return T;
}
Node *Interval_Insert(Node *Root,Node * z)   //红黑树插入,返回树的根
{
    Node * y=T_NIL;
    Node * x=Root;
    while( x != T_NIL)                 //找到结点z要插入的位置
    {
        if ( z->Max > x->Max)     //比较新插入的结点z与结点x的Max大小;
        {
            x->Max = z->Max;
        }
        y=x;
        if (z->INT.low < x->INT.low)
            x = x->left;
        else
            x = x->right;
    }
    z->p = y;
    if ( y == T_NIL)    //插入第一个结点作为根节点的情况
        Root = z;
    else if (z->INT.low < y->INT.low)
        y->left = z;
    else
        y->right = z;

    Root = Interval_Insert_Fixup(Root,z);    //插入完毕后,对红黑树的颜色进行修正
    return Root;
}
Node * Establish(int A[][2],int len)   //建立红黑树
{
   Node * T,*node;
   int i=0;
   node=NULL;
   T_NIL=(Node *)malloc(sizeof(Node));  //建立T_NIL结点
   T_NIL->p=NULL;
   T_NIL->left=NULL;
   T_NIL->right=NULL;
   T_NIL->INT.low=-1;
   T_NIL->color=black;
   T_NIL->Max=0;
   T=T_NIL;
   for (i=0;i<len;i++)
   {
       node =(Node *)malloc(sizeof(Node));
       node->p =T_NIL;
       node->left=T_NIL;
       node->right=T_NIL;
       node->INT.low=A[i][0];         //以INT.low左作为关键字
       node->INT.high=A[i][1];
       node->Max = A[i][1];
       node->color=red;
       T=Interval_Insert(T,node);
   }
   return T;
}

void RB_Transplant(Node **T,Node * u,Node * v)  //结点替代函数
{
  if (u->p == T_NIL)
      *T = v;
  else if (u == u->p->left)
     u->p->left = v;
  else
     u->p->right = v;
  v->p = u->p;               //此处赋值无条件,v如果是T_NIL也要进行赋值
}
Node* Interval_Delete_Fixup(Node * T,Node * x)
{
    Node *w=NULL;
  while( x != T && x->color == black)      //循环迭代处理
  {
      if ( x == x->p->left )
      {
        w = x->p->right;
        if (w->color == red)             // case 1 ------> case 2 , case 3 ,case 4
        {
            w->color = black;
            x->p->color =red;
            Left_Rotate(&T,x->p);
            w = x->p->right;
        }
        if ( w->left->color == black && w->right->color == black ) //case 2 ------>go on / stop
        {
            w->color = red;
            x = x->p;
        }
        else if ( w->right->color == black)   // case 3 ---->case 4---->stop
        {
           w->left->color = black;
           w->color =red ;
           Right_Rotate(&T,w);

           w = x->p->right ;                   //转成case 4处理
           w->color = x->p->color;
           x->p->color = black;
           w->right->color = black;
           Left_Rotate(&T,x->p);
           x = T;
        }
        else                               // case 4 ------------------->stop
        {
            w->color = x->p->color;
            x->p->color = black;
            w->right->color = black;
            Left_Rotate(&T,x->p);
            x = T;
        }
      }
      else
      {

          w = x->p->left;

          if (w->color == red)                // case 1 ------> case 2 , case 3 ,case 4
          {
              w->color = black;
              x->p->color =red;
              Right_Rotate(&T,x->p);
              w = x->p->left;
          }
          if ( w->right->color == black && w->left->color == black ) //case 2 ------>go on/stop
          {

              w->color = red;
              x = x->p;
          }
          else if ( w->left->color == black)      // case 3 -----> case 4----->stop
          {
              w->right->color = black;
              w->color =red ;
              Left_Rotate(&T,w);

              w = x->p->left ;                    //转成case 4处理
              w->color = x->p->color;
              x->p->color = black;
              w->left->color = black;
              Right_Rotate(&T,x->p);
              x = T;
          }
          else                                  // case 4 -------------->stop
          {
              w->color = x->p->color;
              x->p->color = black;
              w->left->color = black;
              Right_Rotate(&T,x->p);
              x = T;
          }
      }
  }

  x->color = black;                       //可能由case2退出,那把x涂黑即可,见分析!也可能有case4退出,把根节点涂黑
 return T;
}
Node * Interval_Delete(Node *T ,Node *z)
{

    Node * x =NULL;
    Node * y =z;
    Node * temp = y->p;
    enum colors y_original_color = y->color;   //记录下删除前z的颜色
    if ( z->left == T_NIL)                     //左子树不存在的情况
    {
        x = z->right;
        RB_Transplant(&T,z,z->right);

    }
    else if ( z->right == T_NIL)              //右子树不存在
    {
       x = z->left;
       RB_Transplant(&T,z,z->left);

    }
    else                                     //左右都存在的情况
    {
       y = Tree_Minimum(z->right);            //找到后继y
       temp = y->p;
       y_original_color = y->color;           //记录下y转移前的颜色
       x = y->right;
       if ( y->p == z)                       //如果y是z的子结点
       {
         x->p = y;
       }
       else
       {
           RB_Transplant(&T,y,y->right);    //如果y不是z的子结点,用y的右子树代替y的位置
           y->right = z->right;
           y->right->p = y;
       }
       RB_Transplant(&T,z,y);           //y替代z的位置 ,不论y是不是T_NIL
       y->left = z->left;
       y->left->p = y;
       y->color = z->color;             //把y的颜色改成z的颜色
    }

    while( temp != T_NIL )             //从要删除的结点或其后继开始向上修复红黑树
    {
            temp->Max = GetMax(temp->left->Max,temp->right->Max,temp->INT.high);
            temp = temp->p;
    }

    if ( y_original_color == black)   //判断y的颜色,若为黑色,需要修复
        T=Interval_Delete_Fixup(T,x);

    return T;
}

Node * Tree_Search(Node *T ,int k)  //寻找数k是否在树中,且返回数k的地址
{   

    while(T != T_NIL && T->INT.low != k)
    {
        if ( k < T->INT.low)
            T=T->left;
        else
            T=T->right;
    }

    if ( T == T_NIL)
    {
        return NULL;
    }

    else
    {
        return T;
    }

}
Node * Interval_Search(Node *T ,struct Interval *i)  //寻找数k是否在树中,且返回数k的地址
{
    Node * x = T;
    while(x != T_NIL && ((x->INT.high < i->low)||(x->INT.low > i->high)))  //不重叠
    {
        if (x->left != T_NIL && x->left->Max >= i->low)  //在其左子树中搜索
        {
            x = x->left;
        }
        else
        {
            x = x->right;
        }
    }

    return x;
}
void main()
{
    int A[][2]={0,3,                   //区间
                5,8,
                6,10,
                8,9,
                15,23,
                16,21,
                17,19,
                19,20,
                25,30,
                26,26};

    int length = sizeof(A)/sizeof(A[0]); //数组区间的长度
    Node *T,*temp;
    struct Interval i;
    i.low = 22;
    i.high = 25; 

    T=Establish(A,length);        //建立红黑树,返回根节点T
    printf("中序遍历:\n");
    Inorder_Tree_Walk(T);printf("\n");   //中序遍历输出
    printf("-----------删除操作后-------------\n");
    T=Interval_Delete(T,Tree_Search(T,6));
    printf("中序遍历:\n");
    Inorder_Tree_Walk(T);
    printf("\n");

    temp = Interval_Search(T,&i);
    printf("____%d___%d__", temp->INT.low,temp->INT.high);

}
时间: 2024-10-25 14:15:21

算法导论--动态顺序统计与区间树的相关文章

算法学习——动态图连通性(线段树分治+按秩合并并查集)

在考场上遇到了这个的板子题,,,所以来学习了一下线段树分治 + 带撤销的并查集. 题目大意是这样的:有m个时刻,每个时刻有一个加边or撤销一条边的操作,保证操作合法,没有重边自环,每次操作后输出当前图下所有联通块大小的乘积. 首先观察到如果没有撤销操作,那么直接用并查集就可以维护,每次合并的时候乘上要合并的两个并查集大小的逆元,然后乘上合并之后的大小即可. 那么来考虑撤销,观察到如果并查集不带路径压缩,应该是可以处理撤销操作的. 但我们并不能直接做,因为并查集的撤销必须按顺序来,就相当于每次合并

算法导论:字符统计问题

在ACM之家看到一道有趣的算法题,好多类似华为,Google等IT巨鳄,都以此为模板来考察实习生. (1)华为的考题形式是:通过键盘输入一串小写字母(a~z)组成的字符串,编写一个字符串过滤程序,若字符串中出现多个相同的字符,将非首次出现的字符过滤掉.如,输入字符串“abacacde”,则输出的过滤结果为“abcde”. 不是很难的问题,采用枚举法,一个二重循环就可搞定.示例代码如下: 1 package org.warnier.zhang.demo; 2 3 public class Stri

算法导论 第十八章;B 树

B树是为磁盘或其他直接存取辅助存储设备而设计的一种平衡查找树.B树的"分支因子"可能很大,即每个节点可以有很多子女.这一因子由所用磁盘特性所决定,并且可以降低磁盘I/O操作次数.许多数据库系统都使用B树或B树的变形来存储信息. B树结构形式如下: 其特点: 1)每个节点x有以下域: a)  x.n:当前存储在节点x中的关键字 b) x.n 个key值,以非降序顺序存放,即 x.key(1) ≤ x.key(2) ≤ ... ≤ x.key(x.n) c) x.leaf:bool型,若为

算法系列笔记5(扩展数据结构-动态顺序统计和区间树)

在编程中,我们往往使用已有的数据结构无法解决问题,这是不必要急着创建新的数据结构,而是在已有数据结构的基础上添加新的字段.本节在上一次笔记红黑树这一基础数据结构上进行扩展,得出两个重要的应用-动态顺序统计和区间树. 动态顺序统计 在算法系列笔记2中我们在线性时间内完成了静态表的顺序统计,而这里我们在红黑树上进行扩展,在O(lgn)时间内完成该操作,主要包括返回第i 排名的元素os_select(i)和给定一个元素x,返回其排名(os_rank(x)). 思想:添加新项:在红黑树的结点上记录下该结

算法导论笔记——第十二~十四章 数据结构(二)树

第十二章 二叉搜索树 >=左子树的所有key,<=右子树的所有key 在一棵高度为h的二叉搜索树上,动态集合上的操作SEARCH,MINIMUM,MAXIMUM,SUCCESSOR,PREDECESSOR,INSERT和DELETE可以在O(h)时间内完成. h>=(lgn向下取整) 和快速排序算法一样,其平均性能更接近于最好情形. 随机构建二叉搜索树期望高度为O(lgn). 各种操作请自行查阅. 第十三章 红黑树 是一种(近似)平衡的二叉搜索树.可以保证在最坏情况下基本动态集合操作的时

算法导论读书笔记(16)

算法导论读书笔记(16) 目录 动态顺序统计 检索具有给定排序的元素 确定一个元素的秩 区间树 步骤1:基础数据结构 步骤2:附加信息 步骤3:维护信息 步骤4:设计新操作 动态顺序统计 之前介绍过 顺序统计 的概念.在一个无序的集合中,任意的顺序统计量都可以在 O ( n )时间内找到.而这里我们将介绍如何在 O ( lg n )时间内确定任意的顺序统计量. 下图显示的是一种支持快速顺序统计量操作的数据结构.一棵 顺序统计树 T 通过在红黑树的每个结点中存入附加信息而成.在一个结点 x 内,增

算法导论读书笔记-第十四章-数据结构的扩张

算法导论第14章 数据结构的扩张 一些工程应用需要的只是标准数据结构, 但也有许多其他的应用需要对现有数据结构进行少许的创新和改造, 但是只在很少情况下需要创造出全新类型的数据结构, 更经常的是通过存储额外信息的方法来扩张一种标准的数据结构, 然后对这种数据结构编写新的操作来支持所需要的应用. 但是对数据结构的扩张并不总是简单直接的, 因为新的信息必须要能被该数据结构上的常规操作更新和维护. 14.1 动态顺序统计 顺序统计树(order-static tree) : 在红黑树的基础上, 在每个

《算法导论》读书笔记(五)

摘要: 本章介绍了二叉查找树的概念及操作.主要内容包括二叉查找树的性质,如何在二叉查找树中查找最大值.最小值和给定的值,如何找出某一个元素的前驱和后继,如何在二叉查找树中进行插入和删除操作.在二叉查找树上执行这些基本操作的时间与树的高度成正比,一棵随机构造的二叉查找树的期望高度为O(lgn),从而基本动态集合的操作平均时间为θ(lgn). 1.二叉查找树 二叉查找树是按照二叉树结构来组织的,因此可以用二叉链表结构表示.二叉查找树中的关键字的存储方式满足的特征是:设x为二叉查找树中的一个结点.如果

算法导论.pdf

下载地址:网盘下载 内容简介  · · · · · · 在有关算法的书中,有一些叙述非常严谨,但不够全面:另一些涉及了大量的题材,但又缺乏严谨性.本书将严谨性和全面性融为一体,深入讨论各类算法,并着力使这些算法的设计和分析能为各个层次的读者接受.全书各章自成体系,可以作为独立的学习单元:算法以英语和伪代码的形式描述,具备初步程序设计经验的人就能看懂:说明和解释力求浅显易懂,不失深度和数学严谨性. 全书选材经典.内容丰富.结构合理.逻辑清晰,对本科生的数据结构课程和研究生的算法课程都是非常实用的教