【算法学习】【数据结构】treap的构建和使用

Treap的构建和使用

BY liukeke

一、定义

叶子我没有找到定义,只是满天飞的人们提到的词,Treap=Tree+Heap,就是让二叉排序树在Heap的规则下进行一些简单的翻转,由于Heap翻转所以靠的关键字段Aux是随机生成的,所以在满足BST的同时,平衡又是随即建立起来的,消除了BST由于有序数据的插入退化的情况,所以Treap在有些地方也被称作随机二叉树Randomize Binary Sort Tree

Treap的翻转中,只有两种翻转:左旋和右旋,比起AVL树和扩展树(Splay Tree)的翻转要简单得多,少得多,很容易掌握。

而且由于随机化,不会出现退化现象,平均的插入、删除和查找效率都为clip_image002[6]

其中,在每个节点上都增加了一个用于维持Heap特性的域Aux,且为随机值,定义如下:

Type

  TTree = ^Tree;

  Tree = Record

    Data,Aux:longint;

    Lch,Rch:TTree;

  End;

 

Var Root:TTree;

 

二、实现

1、翻转

这里的翻转很简单,图示就能表示清楚。而且是两个互为镜像的操作。

记住,左旋是让P降落到比它小的位置上,右旋是让P降落到比它大的位置上。

右旋:

clip_image003[7]

 


            P                               S

 

 

       S                                        P

 

                3                       1

 

    1      2                                 2      3

 

左旋:

clip_image004[6]

 


          P                                  S

 

 

              S                         P

 

      1

 

 

           2      3                  1      2

 

代码如下。

Procedure Rotate_R(Var P:TTree);

Var S:TTree;

  S:=P^.lch;

  P^.lch:=S^.rch;

  S^.rch:=P;

  P:=S;

End;

 

Procedure Rotate_L(Var P:TTree);

Var S:TTree;

Begin

  S:=P^.rch;

  P^.rch:=S^.lch;

  S^.lch:=P;

  P:=S;

End;

 

2、插入

插入的时候,通过二分找到当前数据的位置,然后插入。插入之后,给当前节点的Aux域给一个随机值,然后根据这个随机值,对二叉树进行翻转,这里很像Heap(根的Aux值一定<=儿子的Aux),所以叫Treap。此处给出的是递归算法。(没有给出感觉上效率更高的迭代算法,是因为……我写不出来,太麻烦了)

 

Procedure Insert(Var r:TTree;x:longint);

Begin

  If r=nil then

  Begin

    New(r); r^.data:=x; r^.Aux:=Random(MaxLongint);

    R^.lch:=nil; r^.rch:=nil;

  End

  Else if x<r^.data then

  Begin

    Insert(r^.lch,x);

    If r^.lch^.Aux<r^.Aux then Rotate_R(r);

  End

  Else begin

    Insert(r^.rch,x);

    If r^.rch^.Aux<r^.Aus then Rotate_L(r);

  End;

End;

 

3、删除

删除和BST很相似,需要分类讨论。

情况一:没有儿子,直接删除;

情况二:有一个儿子,将儿子放在原来其所在位置;

情况三:有两个儿子,此时比较两个儿子随机值Aux的大小而进行旋转,向较大的一个儿子的方向旋转,将较小的儿子旋转上去。经过不停的旋转,到最后一定会转变为情况一或情况二(想想为什么?),然后进行删除,也是用递归写法。

 

Procedure Delete(Var P:TTree;x:longint);

Begin

 If p=nil then exit;

 if p^.data=x then

 begin

    If (p^.lch=nil) or (p^.lch=nil) then

    Begin

      If p^.rch=nil then P:=P^.lch else P:=P^.rch;

    End

    Else begin

      If p^.lch^.Aux<p^.rch^.Aux then

      Begin

        Rotate_R(P);

        Delete(p^.lch,x);  //此时要删除的节点就成为了P的左儿子,下面同理

      End

      Else begin

        Rotate_L(P);

        Delete(p^.rch,x);

      End;

    End;

  End

  Else

    If x<p^.data then delete(p^.lch,x)

                 Else delete(p^.rch,x);

End;

 

需要说明的是,在这里,P一定要是在树上的指针,例如:Delete(var root^.lch,key);否则对指针而言,下面的东西会丢失的。

其实用数组模拟指针是个很不错的办法,但是叶子我比较偏爱指针,所以都写的指针代码,感兴趣的可以看一下张后面附带的用数组写的Treap

 

3、搜索。

搜索就完全和BST一模一样了,但是不会发生退化成链条的现象,这就是引入一点点小操作给BST带来的变革,也是一种简单的消除数据顺序对BST的影响,克服了缺点。

 

三、总结

相比较AVL树、红黑书和伸展树,Treap真的是小巧玲珑,理解简单,写起来也简单,所以他是一种很有的数据结构。

需要强调的是,最好在使用之前打开随机化开关Randomize;让随机的更全面,也就让它更灵活。

posted @ 2011-04-14 09:15  liukee  阅读(964)  评论(0编辑  收藏  举报