splay学习小结&常用基本操作模板

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.
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值