1
这几天没有什么题目做,然后就开始乱看题解33,然后看到一题的题解有用到平衡树,但是我太弱了不会,所以决定趁这段时间比较闲学一波splay
2
平衡树是一种每一个叶子节点深度差的绝对值不超过1的二叉搜索树,而二叉搜索树满足对于每一个非叶子节点,ta的左子树中的节点权值比这个节点权值要小,而右子树中的节点权值比ta要大。splay就是一个通过玄学方法来维护这棵二叉搜索树的算法
3
首先平衡树有几种基本操作,假设我们现在要对节点x进行旋转,要使得在平衡树性质不变的情况下,使得x和它父亲的父子关系发生变化(也就是x变成x原来父亲的父亲)
第一种情况:x节点的父亲就是根节点
如上图,从左图到右图的转换我们称为“右旋”,也就是zig;相似的,从右边到左边的转换称为“左旋”,也就是zag。
我们观察一发它是怎么旋转的,以右旋为例,如果我们使得p变为x的儿子,那么x就有三个儿子了,但是我们发现这是p节点没有左儿子(左儿子x变成了p节点的父亲),所以可以把x节点的左儿子B当做p节点的右儿子,同时其他父子关系不发生改变,这样我们就完成了一次右旋。左旋也是一样的
第二种情况:
如上图,此时x的父亲p不是根节点,而且x,p都是其父亲节点的右儿子,所以我们进行zig-zig操作,也就是先对p节点进行zig操作,然后再对x节点进行zig操作,zag-zag操作也是同理
第三种情况:
如上图,此时x节点为p节点的左儿子,而p节点是g节点的右儿子,这是我们进行zig-zag操作,当然在这里虽然是这么称呼,但是是先对x节点进行一次zag,再对x节点进行一次zig操作,与第二种情况不同的是这里两次旋转都是对x节点的
那么splay是什么呢?
其实splay(x)就代表一值对x节点进行上述的操作,直到x节点变成根节点
然后记得出了search操作之外做完一些操作之后要splay一下
4
各种操作如何通过splay来具体的实现呢?
1:插入节点
分几种情况:
如果现在树中没有节点那么加入这个新节点并且把它当做根
如果这个节点在树中已经出现过那么只要把对应节点的记录出现次数的值和以那个节点为根可以形成的子树大小+1就可以了
如果这个节点在树中没有出现过我们对树进行一次search(也就是二叉搜索树的search),然后把这个节点当做search返回值的儿子就可以了
2:求最大/最小值
就是search(maxlongint)(最大值)或者是search(-maxlongint)(最小值)的返回值嘛
3:删除
假设我们现在要删除x节点,那么怎么样才能在删除x节点后还使得树保证条件呢?
可以先把x节点splay一下,然后在x的左子树中找一个最大的点,splay一下这个点,然后把这个点当做新的根就可以了(这个点显然一开始是没有右儿子的,所以可行)
4:查询x数的排名
我们先search一下x这个数找到它在树中的具体位置(比如说是a节点),然后对a点splay,左/右子树的大小+1即是其从小到大/从大到小排名
5:查询排名为x的数
如果查询的是排名第x小的,那么我们从根节点开始,如果当前的x∈[a左儿子子树大小+1,a左子树大小+a节点的value值(也就是这个权值出现的次数)],那么第x小的数就是a节点对应的那个数
如果x< a左儿子子树大小+1,那么就是说排名第x小的点位置在a节点左儿子为根的那颗子树中,那么我们继续往左子树方向找
如果x> a左儿子子树大小+a节点的value值,那么第x小的位置就在a节点右儿子为根的那颗子树中,我们先把x-(a左儿子子树大小+a节点的value值)后往右儿子的方向找
最后把asplay一下(别问我为什么我也不知道~)
6:查询a(数值)的前驱/后继
首先找到a对应的位置x,然后把x节点splay一下,前驱就是x左儿子里最大的那个,后继就是x右儿子里最小的那个
贴代码
var
father,value,count,data:array[0..100005]of longint;
son:array[0..100005,1..2]of longint;
i,j,k,l,m,n,x,y,z,t1,t2,root,tot:longint;
procedure rotate(x,w:longint);
var
y:longint;
begin
y:=father[x];
count[y]:=count[y]-count[x]+count[son[x,w]];
count[x]:=count[x]+count[y]-count[son[x,w]];
son[y,3-w]:=son[x,w];
if son[x,w]<>0 then father[son[x,w]]:=y;
father[x]:=father[y];
if father[y]<>0 then
if y=son[father[x],1] then son[father[x],1]:=x else son[father[x],2]:=x;
father[y]:=x;
son[x,w]:=y;
end;
procedure splay(x:longint);
var
y:longint;
begin
while father[x]<>0 do
begin
y:=father[x];
if father[y]=0 then
begin
if x=son[y,1] then rotate(x,2) else rotate(x,1);
end
else
begin
if son[father[y],1]=y then
begin
if x=son[y,1] then begin rotate(y,2); rotate(x,2); end
else begin rotate(x,1); rotate(x,2); end;
end else
begin
if x=son[y,1] then begin rotate(x,2); rotate(x,1); end
else begin rotate(y,1); rotate(x,1); end;
end;
end;
end;
root:=x;
end;
function search(x,w:longint):longint;
begin
while data[x]<>w do
begin
if data[x]=w then exit(x);
if w<data[x] then
begin
if son[x,1]=0 then break;
x:=son[x,1];
end else
begin
if son[x,2]=0 then break;
x:=son[x,2];
end;
end;
exit(x);
end;
procedure insert(w:longint);
var
k,kk:longint;
flag:boolean;
begin
if tot=0 then
begin
tot:=1;
father[1]:=0; value[1]:=1; count[1]:=1; data[1]:=w; root:=1;
exit;
end;
k:=search(root,w);
flag:=false;
if data[k]=w then
begin
inc(value[k]); kk:=k; flag:=true;
end
else
begin
inc(tot);
father[tot]:=k; data[tot]:=w; value[tot]:=1; count[tot]:=1;
if data[k]>w then son[k,1]:=tot else son[k,2]:=tot;
end;
while k<>0 do
begin
inc(count[k]);
k:=father[k];
end;
if flag=true then splay(kk) else splay(tot);
end;
function extreme(x,w:longint):longint;
var
cc:array[1..2]of longint=(maxlongint,-maxlongint);
k:longint;
begin
k:=search(x,cc[w]);
extreme:=data[k];
splay(k);
end;
procedure delete(w:longint);
var
y,k:longint;
begin
k:=search(root,w);
if data[k]<>w then splay(k) else
begin
splay(k);
if value[k]>1 then begin dec(value[k]); dec(count[k]); end
else
if son[k,1]=0 then
begin
y:=son[k,2]; data[k]:=0; value[k]:=0; count[k]:=0; son[k,2]:=0;
root:=y; father[y]:=0;
end else
begin
father[son[k,1]]:=0;
y:=extreme(son[k,1],1);
son[root,2]:=son[k,2];
count[root]:=count[root]+count[son[k,2]];
if son[root,2]<>0 then father[son[root,2]]:=root;
value[k]:=0; son[k,1]:=0; son[k,2]:=0; count[k]:=0; data[k]:=0;
end;
end;
end;
function findnum(x:longint):longint;
var y:longint;
begin
y:=search(root,x);
splay(y);
findnum:=count[son[root,1]]+1;
end;
function kth(x,w:longint):longint;
var
i:longint;
begin
i:=root;
while ((x<count[son[i,w]]+1) or (x>count[son[i,w]]+value[i])) and (i<>0) do
begin
if x>count[son[i,w]]+value[i] then
begin
x:=x-count[son[i,w]]-value[i];
i:=son[i,3-w];
end else i:=son[i,w];
end;
kth:=i;
splay(i);
end;
function pred(x:longint):longint;
var
k:longint;
begin
k:=search(root,x); splay(k);
if data[k]<x then exit(data[k]);
exit(extreme(son[k,1],1));
end;
function succ(x:longint):longint;
var
k:longint;
begin
k:=search(root,x); splay(k);
if data[k]>x then exit(data[k]);
exit(extreme(son[k,2],2))
end;
begin
//assign(input,'splay.in'); reset(input);
readln(n);
for i:=1 to n do
begin
readln(t1,t2);
if t1=1 then insert(t2) else
if t1=2 then delete(t2) else
if t1=3 then writeln(findnum(t2)) else
if t1=4 then writeln(data[kth(t2,1)]) else
if t1=5 then writeln(pred(t2)) else writeln(succ(t2));
end;
//close(input);
end.