VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • c#数据结构之二叉树红黑树Red Black Tree 二

制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
 

  下面把代码贴出来,如果理解了上面所讲内容是很容易读懂这些代码的。

usingSystem;
namespacePaintBST
{
  publicclassRBTree:IBinaryTree//实现画树接口
  {  //成员变量
    privateNode_head;//头指针
    privateNode[]path=newNode[32];//记录访问路径上的结点
    privateintp;//表示当前访问到的结点在_path上的索引
    INodeIBinaryTree.Head//显式接口实现
    {
      get{return(INode)_head;}
   }
    publicboolAdd(intvalue)//添加一个元素
    { //如果是空树,则新结点成为二叉排序树的根
      if(_head==null)
      {
        _head=newNode(value);
        _head.IsRed=false;
        returntrue;
      }
      p=0;
      //parent为上一次访问的结点,current为当前访问结点
      Nodeparent=null,current=_head;
      while(current!=null)
      {
        path[p++]=current;//将路径上的结点插入数组
        //如果插入值已存在,则插入失败
        if(current.Data==value)
        {
          returnfalse;
        }
        parent=current;
        //当插入值小于当前结点,则继续访问左子树,否则访问右子树
        current=(value<parent.Data)?parent.Left:parent.Right;
      }
      current=newNode(value);//创建新结点
       current.IsRed=true;
      if(value<parent.Data)//如果插入值小于双亲结点的值
       {
        parent.Left=current;//成为左孩子
       }
      else//如果插入值大于双亲结点的值
       {
        parent.Right=current;//成为右孩子
       }
      if(!parent.IsRed)
      {
        returntrue;
      }
      path[p]=current;
      //回溯并旋转
      while((p-=2)>=0)//现在p指向插入点的祖父结点
      {
        NodegrandParent=path[p];
        parent=path[p+1];
        if(!parent.IsRed)
        {
          break;
        }
        Nodeuncle=grandParent.Left==parent?grandParent.Right:grandParent.Left;
        current=path[p+2];
        if(IsRed(uncle))//叔父存在并且为红色的情况
         {
          parent.IsRed=false;
          uncle.IsRed=false;
          if(p>0)//如果祖父不是根结点,则将其染成红色
           {
            grandParent.IsRed=true;
          }
        }
        else//叔父不存在或为黑的情况需要旋转
         {
          NodenewRoot;
          if(grandParent.Left==parent)//如果当前结点及父结点同为左孩子或右孩子
           {
            newRoot=(parent.Left==current)?LL(grandParent):LR(grandParent);
          }
          else
          {
            newRoot=(parent.Right==current)?RR(grandParent):RL(grandParent);
          }
          grandParent.IsRed=true;//祖父染成红色
          newRoot.IsRed=false;//新根染成黑色
          //将新根同曾祖父连接
          ReplaceChildOfNodeOrRoot((p>0)?path[p-1]:null,grandParent,newRoot);
          returntrue;//旋转后不需要继续回溯,添加成功,直接退出
         }
      }
      returntrue;
    }
    //旋转根旋转后换新根
    privatevoidReplaceChildOfNodeOrRoot(Nodeparent,Nodechild,NodenewChild)
    {
      if(parent!=null)
      {
        if(parent.Left==child)
        {
          parent.Left=newChild;
        }
        else
        {
          parent.Right=newChild;
        }
      }
      else
      {
        _head=newChild;
      }
    }
    privatestaticboolIsBlack(Nodenode)
    {
      return((node!=null)&&!node.IsRed);
    }
    privatestaticboolIsNullOrBlack(Nodenode)
    {
      if(node!=null)
      {
        return!node.IsRed;
      }
      returntrue;
    }
    privatestaticboolIsRed(Nodenode)
    {
      return((node!=null)&&node.IsRed);
    }
    //删除指定值
    publicboolRemove(intvalue)
    {
      p=-1;
      //parent表示双亲结点,node表示当前结点
      Nodenode=_head;
      //寻找指定值所在的结点
      while(node!=null)
      {
        path[++p]=node;
        //如果找到,则调用RemoveNode方法删除结点
        if(value==node.Data)
        {
          RemoveNode(node);//现在p指向被删除结点
          returntrue;//返回true表示删除成功
        }
        if(value<node.Data)
        { //如果删除值小于当前结点,则向左子树继续寻找
          node=node.Left;
        }
        else
        { //如果删除值大于当前结点,则向右子树继续寻找
          node=node.Right;
        }
      }
      returnfalse;//返回false表示删除失败
    }
    //删除指定结点
    privatevoidRemoveNode(Nodenode)
    {
      Nodetmp=null;//tmp最终指向实际被删除的结点
      //当被删除结点存在左右子树时
      if(node.Left!=null&&node.Right!=null)
      {
        tmp=node.Left;//获取左子树
        path[++p]=tmp;
        while(tmp.Right!=null)//获取node的中序遍历前驱结点,并存放于tmp中
        { //找到左子树中的最右下结点
          tmp=tmp.Right;
          path[++p]=tmp;
       }
        //用中序遍历前驱结点的值代替被删除结点的值
        node.Data=tmp.Data;
      }
      else
      {
        tmp=node;
      }
      //当只有左子树或右子树或为叶子结点时
      //首先找到惟一的孩子结点
      NodenewTmp=tmp.Left;
      if(newTmp==null)//如果只有右孩子或没孩子
       {
        newTmp=tmp.Right;
      }
      if(p>0)
      {
        Nodeparent=path[p-1];
        if(parent.Left==tmp)
        { //如果被删结点是左孩子
          parent.Left=newTmp;
        }
        else
        { //如果被删结点是右孩子
          parent.Right=newTmp;
        }
        if(!tmp.IsRed&&IsRed(newTmp))
        {
          newTmp.IsRed=false;
          return;
        }
      }
      else //当删除的是根结点时
       {
        _head=newTmp;
        if(_head!=null)
        {
          _head.IsRed=false;
        }
        return;
      }
      path[p]=newTmp;
      //如果被删除的是红色结点,则它必定是叶子结点,删除成功,返回
      if(IsRed(tmp))
      {
        return;
      }
      //删除完后进行旋转,现在p指向实际被删除的位置,这个位置可能为空,tmp指向被删除的旧点,
      while(p>0)
      { //剩下的是双黑的情况
        //首先找到兄弟结点
        Nodecurrent=path[p];
        Nodeparent=path[p-1];
        boolcurrentIsLeft=(parent.Left==current);
        Nodesibling=currentIsLeft?parent.Right:parent.Left;
        //红兄的情况,需要LL旋转或RR旋转
        if(IsRed(sibling))
        {
          NodenewRoot;
          if(currentIsLeft)
          {
            newRoot=RR(parent);
          }
          else
          {
            newRoot=LL(parent);
          }
          ReplaceChildOfNodeOrRoot(p>1?path[p-2]:null,parent,newRoot);
          sibling.IsRed=false;
          parent.IsRed=true;
          //回溯点降低
          path[p-1]=newRoot;
          path[p]=parent;
          path[++p]=current;
        }
        else//黑兄的情况
         {
          //黑兄二黑侄
          if(IsNullOrBlack(sibling.Left)&&IsNullOrBlack(sibling.Right))
         { //红父的情况
            if(parent.IsRed)
          {
              parent.IsRed=false;
              sibling.IsRed=true;
              if(current!=null)
              {
                current.IsRed=false;
              }
              break;//删除成功
            }
            else//黑父的情况
            {
              parent.IsRed=IsRed(current);
              if(current!=null)
              {
                current.IsRed=false;
              }
              sibling.IsRed=true;
              p--;//需要继续回溯
            }
         }
         else//黑兄红侄
          {
            NodenewRoot;
            if(currentIsLeft)//兄弟在右边
              {
              if(IsRed(sibling.Right))//红侄在右边
                { //RR型旋转
                newRoot=RR(parent);
                sibling.Right.IsRed=false;
              }
              else
              { //RL型旋转
                newRoot=RL(parent);
              }
            }
            else//兄弟在左边
             {
              if(IsRed(sibling.Left))
              { //LL型旋转
                newRoot=LL(parent);
                sibling.Left.IsRed=false;
              }
              else
              { //LR型旋转
                newRoot=LR(parent);
              }
            }
            if(current!=null)
            {
              current.IsRed=false;
            }
            newRoot.IsRed=parent.IsRed;
            parent.IsRed=false;
            ReplaceChildOfNodeOrRoot(p>1?path[p-2]:null,parent,newRoot);
            break;//不需要回溯,删除成功
          }
        }
      }
    }
    //root为旋转根,rootPrev为旋转根双亲结点
    privateNodeLL(Noderoot)//LL型旋转,返回旋转后的新子树根
    {
      Nodeleft=root.Left;
      root.Left=left.Right;
      left.Right=root;
      returnleft;
    }
    privateNodeLR(Noderoot)//LR型旋转,返回旋转后的新子树根
    {
      Nodeleft=root.Left;
      Noderight=left.Right;
      root.Left=right.Right;
      right.Right=root;
      left.Right=right.Left;
      right.Left=left;
      returnright;
    }
    privateNodeRR(Noderoot)//RR型旋转,返回旋转后的新子树根
    {
      Noderight=root.Right;
      root.Right=right.Left;
      right.Left=root;
      returnright;
    }
    privateNodeRL(Noderoot)//RL型旋转,返回旋转后的新子树根
    {
      Noderight=root.Right;
      Nodeleft=right.Left;
      root.Right=left.Left;
      left.Left=root;
      right.Left=left.Right;
      left.Right=right;
      returnleft;
    }
  }
}

  完成红黑树后,做了一个比较粗糙的测试程序,对我自己实现的红黑树RBTree,C#类库中的红黑树TreeSet和我自己实现的AVL树AVLTree进行了简单的测试,为了公平起见,我把TreeSet改成了整型版,这样大家都站在了同一起跑线上。考虑到垃圾回收,这样的测试并不是很精确、科学,但也能说明一些问题。以后我会专门写程序对各种常见的查找数据结构进行测试

 

测试项目 RBTree TreeSet AVLTree
200000个整数顺序插入(全部命中) 0.4375 0.4844 0.3437
200000个整数顺序插入后顺序删除(全部命中,只对删除部分计时) 0.25 0.5 0.20
200000个整数随机插入(全部命中) 0.4375 0.5469 0.5
200000个整数随机插入后顺序删除(全部命中,只对删除部分计时) 0.5 0.7343 0.4219
200000个整数顺序插入(一半命中) 0.297 0.344 0.234
100000个整数随机插入后顺序删除(一半命中,只对删除部分计时) 0.094 0.203 0.109
后记

  测试结果基本证实了我的想法,惟一意外的是AVL树有两个项目输给了RBTree。在理论上,RBTree在某些方面的确是比AVL树更为优秀,但从程序员的角度来说,红黑树的实现需要调用大量方法,比如结点颜色的判断,这会抵消红黑树的性能优势。国外网站也有针对红黑树和AVL树的测试,结果基本上是AVL树胜出。

  红黑树的动画还有一些bug,整体效果也不尽如人意,我也懒得再改了,写它比写红黑树困难些。写它主要是为了学习Silverlight,这也算是第一个Silverlight动画作品,东西弄懂了就不想再动了。打算将来更深入地了解Silverlight后再全面修改这个动画。



相关教程