Treap

2 篇文章 0 订阅

  所谓Treap,就是单词Tree和heap的合成词,它是具有堆特性的一颗二叉查找树,它和 笛卡尔树(Cartesian Tree)的形式很像。因为当二叉查找树退化时速度会很满,所以引入了随机优先值的heap,使在Treap树中,根节点的值大于等于左子树的值,小于等于右子树的值,根节点的优先值大于子树的优先值(其实这里优先值纯属个人喜好,大于和小于效果一样)。如下图所示(黑色为权值,红色为优先值):
  Treap
  存储Treap的结构如下:

type treap = record
               rnd,val,size,w:longint; 
               //分别是优先值,权值,以当前节点为根子节点的个数,和当前节点权值相同的个数
               l,r:longint;     
               //分别记录左儿子和右儿子编号,0为空
             end;
var tree:array[0..MAXN] of treap;

1.插入(期望复杂度为 O(log2n) ):
  1.按照二叉查找树的方式插入
  2.根据优先值进行旋转调整

  代码如下(tot为总节点个数): 

procedure insert(var k:longint; x:longint);
  begin
    if k=0 then begin
      inc(tot); k:=tot;
      tree[k].size:=1; tree[k].w:=1
      tree[k].val:=x; tree[k].rnd:=random(MAXD);
      tree[k].l:=0; tree[k].r:=0;
      exit;
    end;
    inc(tree[k].size); 
    if tree[k].val=x then begin inc(tree[k].w); exit; end;
    if x<tree[k].val then begin
      insert(tree[k].l,x);
      if tree[tree[k].l].rnd>tree[k].rnd then right_rotation(k);
      exit;
    end;
    if x>tree[k].val then begin
      insert(tree[k].r,x);
      if tree[tree[k].r].rnd>tree[k].rnd then left_rotation(k);
  end;

  在插入我们看到进行了两种旋转操作,分别是左旋left_rotation和右旋right_rotation:左旋和右旋
  实现代码如下:

procedure left_rotation(var k:longint);
   var tmp:longint;
   begin
     tmp:=tree[k].r; tree[k].r:=tree[tmp].l; tree[tmp].l:=k;
     update(k); update(tmp); k:=tmp;
   end;
procedure right_rotation(var k:longint);
   var tmp:longint;
   begin
     tmp:=tree[k].l; tree[k].l:=tree[tmp].r; tree[tmp].r:=k;
     update(k); update(tmp); k:=tmp;
   end;

  注意到过程中有一个新的函数update,因为我们刚刚只完成了旋转,节点上的数据还需要改变:

procedure update(k:longint);
  begin
    tree[k].size:=tree[tree[k].l].size+tree[tree[k].r].size+tree[k].w;
  end;

  这样插入操作就完成了。

2.删除(期望复杂度为 O(log2n)
  1.找到要删除的点的位置
  2.将其向下旋转至只有一个儿子或为叶子节点时进行删除
  
  实现代码如下:

procedure delete(var k:longint; x:longint);
  begin
    if k=0 then exit;
    if tree[k].val=x then begin
      if tree[k].w>1 then begin
        dec(tree[k].w); dec(tree[k].size); exit;
      end;
      if tree[k].l*tree[k].r=0 then begin
        k:=tree[k].l+tree[k].r;
      end
      else if tree[tree[k].l].rnd>tree[tree[k].r].rnd 
             then begin
               right_rotation(k); delete(k,x);
             end
             else begin
               left_rotation(k); delete(l,x);
             end;
    end
    else if x>tree[x].val
           then begin
             dec(tree[k].size); delete(tree[k].r,x);
           end
           else begin
             dec(tree[k].size); delete(tree[k].l,x);
           end;
  end;

3.查询
  与二叉查找树相同
  
  代码如下:(查找第x大的数)

function query_num(k,x:longint);  
  begin
    if k=0 then exit;
    if x<=tree[tree[k].l].size]
      then exit(query_num(tree[k].l,x))
      else if (x>tree[tree[k].l].size+tree[k].w)
             then exit(query_num(tree[k].r,x-tree[tree[k].l].size-tree[k].w))
             else exit(tree[k].val);
  end;

4.前驱,后继
  与二叉查找树相同
  
  代码如下:

procedure query_pred(k,x:longint);
  begin
    if k=0 then exit;
    if tree[k].val<x then begin
      ans:=k; query_pred(tree[k].r,x);
    end
    else query_pred(tree[k].l,x);
  end;
procedure query_succ(k,x:longint);
  begin
    if k=0 then exit;
    if tree[k].val>x then begin
      ans:=k; query_pred(tree[k].l,x);
    end
    else query_pred(tree[k].r,x);
  end;

参考资料:
1.Treap_百度百科
2.黄学长blog:HZWER:We are OIers|Hello world!My blog

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值