所谓Treap,就是单词Tree和heap的合成词,它是具有堆特性的一颗二叉查找树,它和 笛卡尔树(Cartesian Tree)的形式很像。因为当二叉查找树退化时速度会很满,所以引入了随机优先值的heap,使在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