treap模板and易错点

来源:互联网 发布:sql drop所有表 编辑:程序博客网 时间:2024/05/16 04:58

treap.........tree+heap

具体是给每个节点新给一个id(随机的),维护id的堆序,以堆序维护平衡。

这些资料网上一大把,但是网上没有一个pascal模板(吐血),也没有一个非递归模板(再次吐血),而且全都是我不喜欢的动态指针(终极吐血)..........

.........

在参考了splay 的模版之后,终于把treap的操作弄出来了


注:

第一个节点被我弄成了超级根,权值和id为负无穷大;

a中记录节点的信息,id是维护平衡的随机值,w是节点权值,size域维护的是以该节点为根的子树大小;

l,r数组记录的是 左右节点,-1代表没有子节点;

pre记录父亲节点;

维护的是小根堆堆序。


添加x节点:

procedure insert(x:longint);var st:longint;begin    st:=1;    while true do      begin         inc(a[st].size);         if a[x].w>=a[st].w then            begin               if r[st]=-1 then                 begin                   r[st]:=x;                   pre[x]:=st;                   break;                 end               else st:=r[st];            end         else begin              if l[st]=-1 then                begin                   l[st]:=x;                   pre[x]:=st;                   break;                end              else st:=l[st];             end;      end;    while a[x].id<a[pre[x]].id do        if l[pre[x]]=x then            right(x) else left(x);end;                                 


对x节点右旋和左旋

procedure right(x:longint);var y,z:longint;begin    y:=pre[x];z:=pre[y];    if y=l[z] then l[z]:=x else r[z]:=x;    pre[x]:=z;    l[y]:=r[x];pre[r[x]]:=y;    r[x]:=y;pre[y]:=x;    a[x].size:=a[y].size;    a[y].size:=a[l[y]].size+a[r[y]].size+1;end;procedure left(x:longint);var   y,z:longint;begin    y:=pre[x];z:=pre[y];    if y=l[z] then l[z]:=x else r[z]:=x;    pre[x]:=z;    r[y]:=l[x];pre[l[x]]:=y;    l[x]:=y;pre[y]:=x;    a[x].size:=a[y].size;    a[y].size:=a[l[y]].size+a[r[y]].size+1;end;       



找第k小值:

function findsk(k:longint):longint;var st:longint;begin    st:=1;inc(k);    while true do         if k=a[l[st]].size+1 then exit(a[st].w)           else if k<=a[l[st]].size then st:=l[st]             else begin                    k:=k-a[l[st]].size-1;                    st:=r[st];                  end;end;                      

找第k大值:

function findlk(k:longint):longint;var st:longint;begin    st:=1;    while true do         if k=a[r[st]].size+1 then exit(a[st].w)           else if k<=a[r[st]].size then st:=r[st]             else begin                    k:=k-a[r[st]].size-1;                    st:=l[st];                  end;end;                      

堆式上浮:

procedure high(x:longint);begin   while a[x].id<a[pre[x]].id do       if x=l[pre[x]] then right(x)         else left(x);end;                      

堆式下沉:

procedure low(x:longint);var bj:longint;  begin   while true do    begin       bj:=l[x];if bj=-1 then bj:=r[x];if bj=-1 then exit;       if  (r[x]<>-1) and (a[r[x]].id<a[bj].id) then bj:=r[x];       if  a[bj].id>a[x].id then exit;        if  bj=r[x] then left(bj) else right(bj);    end;end;      

寻找最大最小值:

procedure findmax;begin    st:=r[1];    if st=-1 then writeln('0')    else      begin         while r[st]<>-1 do st:=r[st];         writeln(a[st].what);      end;end;procedure findmin;begin   st:=r[1];   if st=-1 then writeln('0')   else    begin         while l[st]<>-1 do st:=l[st];         writeln(a[st].what);    end;end;        

删除节点:

procedure delete(x:longint);  begin    a[x].id:=maxlongint;     while not((l[x]=-1)and(r[x]=-1))  do  low(x);    if x=l[pre[x]] then l[pre[x]]:=-1 else r[pre[x]]:=-1;  while x<> 1 do    begin          x:=pre[x];        dec(a[x].size);    end;end;                 






treap声称是简单易学的平衡树,其实........编起来不是那么回事 ;

treap虽然比红黑树或AVL好编的多,但是treap由于引入了random,调试将有很大的麻烦:

如果有地方编错了,答案可能每一次都不一样,出错的时刻也会不一样(因为每一次的树的结构都不一样)

这里总结一下常见的错误:

1,左右旋时,注意每个节点的父亲和儿子节点是否都已修改,同时size域要特别注意。

2,添加新节点时,记得先把它的左右儿子设为空!删除节点是也要同样注意子节点设空。

3,由于超级根的存在,找最大最小值时,有一点改动:

      找到最小值时,他不一定是他父亲的左节点(若超级根权值为负无穷),

      找到最大值时,他不一定是他父亲的右节点(若超级根权值为正无穷)。

4,下沉节点时特别注意空节点。


差不多就是这样了。


取最大最小值并删除:
取最大最小值并删除:
取最大最小值并删除:
取最大最小值并删除: