treap模板and易错点

本文介绍了一种Treap的非递归实现方法,并提供了详细的Pascal代码示例。Treap结合了二叉查找树和堆的特点,通过随机分配优先级来保持树的平衡。文章还讨论了插入、删除等操作的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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,下沉节点时特别注意空节点。


差不多就是这样了。


取最大最小值并删除:
取最大最小值并删除:
取最大最小值并删除:
取最大最小值并删除:
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值