您的位置:首页 > 其它

Splay Tree 伸展树

2010-04-20 19:19 260 查看
写在前面:我是巨菜……代码巨丑~请各种大牛不要鄙视……

Splay也就是传说中的伸展树~以前很想学,但是因为旋转比较晕,所以一直没学~这回因为决定学了,所以就看了看,写了写。经历了3个小时的摸索……终于写出来了……

首先说我用的题,是一道专门测平衡树的题:

给出N(N<=1000000)个操作,然后有N行,每行两个数,ch和k
ch=1表示插入一个值为k的数
ch=2表示查询第k小的数
ch=3表示删除值为k的数(k一定存在)
我之前的平衡树速度测试和学习都用的这道题(P.s.据说是陈启峰神牛的数据)。Splay是所以神奇就是因为它的灵活,我还是按照我的习惯来说说这个东西。想看的更具体一点呢,可以去看看sqybi神牛的经典Splay教程《The Magical Splay》(地址:http://www.oibh.org/bbs/viewthread.php?tid=23645&highlight=splay

首先,平衡树的重要操作肯定少不了旋转,Splay的旋转就是个很悲剧的东西。因为splay的精髓在于把一个点旋转到任意一个位置,所以要记录每个点的父亲。但是一旦旋转中出现了父亲,很多东西都很IMBA~

还是一样,两种旋转ZIG和ZAG。ZIG(x)表示当x为它父亲的左儿子时,将x旋转到父亲的位置上的操作,ZAG则反之。(这个东西要认真写,差一点都会出大问题。)

然后就是伸展树的精髓,就是Splay(x,y)过程,就是将x旋转到y的儿子的位置,当y=0的时候旋转到根。这样就一共有6种情况,写写还可以。

然后就是怎么用它来维护树的平衡,说白了就是每次搞完了之后就将操作的那个点旋转到根去就好。所有操作中比较神奇的应该说是删除操作,首先找到您要删除的节点转到根上,然后找到比它大的最小的数和比它小的最大的数。(P.s.说白了就是找前驱和后继),这个时候要看这两个指针是否为空,如果是空,那么直接把root改下就好。反之,如果左右都有,那么将前驱转到根上,然后将后继转到前驱的右儿子上,细心的人这时候就发现,根的右儿子的左儿子只有一个元素,就是我们要删掉的那个元素。这样就结了,干掉这个点就好……

实测情况:



P.s.skyprophet那个是我以前的treap,cqf那个是陈启峰神牛的SBT,splay就是我这会写的东西了。~~

【代码】:

{$M 655360000}
program Project1;
type
ttype=record
data,left,right,father,size,count:longint;
end;
var
tree:array[0..1000000] of ttype;
root,n,i,k,x,tot,now:longint;
procedure modify(p:longint);
begin
tree[p].size:=tree[tree[p].left].size+tree[tree[p].right].size+tree[p].count;
end;
procedure zig(t:longint);
var
p:longint;
begin
p:=tree[t].father;
tree[p].left:=tree[t].right;
if tree[t].right<>0 then
tree[tree[t].right].father:=p;
tree[t].father:=tree[p].father;
if tree[p].father<>0 then
if tree[tree[p].father].left=p then
tree[tree[p].father].left:=t
else tree[tree[p].father].right:=t;
tree[t].right:=p; tree[p].father:=t;
modify(p); modify(t);
end;
procedure zag(t:longint);
var
p:longint;
begin
p:=tree[t].father;
tree[p].right:=tree[t].left;
if tree[t].left<>0 then
tree[tree[t].left].father:=p;
tree[t].father:=tree[p].father;
if tree[p].father<>0 then
if tree[tree[p].father].left=p then
tree[tree[p].father].left:=t
else tree[tree[p].father].right:=t;
tree[t].left:=p; tree[p].father:=t;
modify(p); modify(t);
end;
procedure splay(p,x:longint);
begin
while tree[p].father<>x do
begin
if tree[tree[p].father].father=x then
begin
if tree[tree[p].father].left=p then zig(p)
else zag(p);
end
else
begin
if tree[p].father=tree[tree[tree[p].father].father].left then
begin
if tree[tree[p].father].left=p then
begin zig(tree[p].father); zig(p); end
else begin zag(p); zig(p); end;
end
else
begin
if tree[tree[p].father].left=p then
begin zig(p); zag(p); end
else begin zag(tree[p].father); zag(p); end;
end;
end;
end;
if x=0 then root:=p;
end;
procedure insert(var p:longint;x,fa:longint);
begin
if p=0 then
begin
inc(tot); p:=tot;
tree[p].data:=x; tree[p].left:=0; tree[p].right:=0;
tree[p].father:=fa; tree[p].size:=1; tree[p].count:=1;
now:=p;
end
else if tree[p].data=x then inc(tree[p].count)
else if x<tree[p].data then insert(tree[p].left,x,p)
else insert(tree[p].right,x,p);
modify(p);
end;
function find(p,x:longint):longint;
begin
if tree[p].data=x then exit(p)
else if x<tree[p].data then exit(find(tree[p].left,x))
else exit(find(tree[p].right,x));
end;
procedure delete(x:longint);
var
t1,t2,p:longint;
begin
p:=find(root,x);
splay(p,0);
if tree[p].count>1 then begin dec(tree[p].count); dec(tree[p].size); end
else
begin
t1:=tree[p].left;
while tree[t1].right<>0 do t1:=tree[t1].right;
t2:=tree[p].right;
while tree[t2].left<>0 do t2:=tree[t2].left;
if t1=0 then begin root:=tree[p].right; tree[root].father:=0; exit; end;
if t2=0 then begin root:=tree[p].left; tree[root].father:=0; exit; end;
splay(t1,0); splay(t2,t1); tree[t2].left:=0;
modify(t2); modify(t1);
end;
end;
function findkth(p,x:longint):longint;
begin
if tree[tree[p].left].size>=x then exit(findkth(tree[p].left,x))
else if tree[tree[p].left].size+tree[p].count>=x then
begin
now:=p;
exit(tree[p].data);
end
else exit(findkth(tree[p].right,x-tree[tree[p].left].size-tree[p].count));
end;
begin
assign(input,'sbt.in');
assign(output,'sbt.out');
reset(input); rewrite(output);
readln(n);
root:=0; tot:=0;
for i:=1 to n do
begin
readln(k,x);
case k of
1:begin insert(root,x,0); splay(now,0); end;
2:begin
if tree[root].size>=x then
begin
writeln(findkth(root,x));
splay(now,0);
end
else writeln(0);
end;
3:delete(x);
end;
end;
close(input); close(output);
end.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: