Splay学习笔记

这篇文章是写给自己看的,可能不会在意某些细节

参考博客(关键是有图好理解):https://www.cnblogs.com/cjyyb/p/7499020.html

前言

Splay是平衡树的一种

它跟treap不同的是,它可以智能(不需要去刻意找方向)旋转,也可以实现区间翻转,个人感觉也好写好调一些

是LCT的前置芝士

实现

代码小技巧

#define check(x,y) (a[y].ch[1]==x)
//判断x是y的哪个儿子
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
#define pushup(x) a[x].siz=a[x].cnt+a[ls(x)].siz+a[rs(x)].siz

插入

找到x的位置并插入

in void insert(int x)
{
    int k=root,ff=0;
    while(a[k].ch[(a[k].w<x)] && a[k].w!=x) ff=k,k=a[k].ch[(a[k].w<x)];//找到待插入值的位置 ,并记录改点父亲 
    if(k) a[k].cnt++,a[k].siz++; //若已有之前该点 
    else //新建节点 
    {
        k=++tot;
        if(ff) a[ff].ch[a[ff].w<x]=k;
        a[k].cnt=a[k].siz=1;
        a[k].w=x,a[k].ff=ff;
    }
    Splay(k,0);
}

单点旋转至上一层

旋转时一定要注意更新顺序!!!

in void rotate(int x) //将x向上旋转一层 (更新的顺序很重要!!!) 
{
    int y=a[x].ff; //x的父亲 
    int z=a[y].ff; //x的爷爷 
    int k=check(x,y); //x与y的方向关系 
    a[z].ch[check(y,z)]=x,a[x].ff=z;//先处理x的爷爷 
    a[a[x].ch[!k]].ff=y,a[y].ch[k]=a[x].ch[!k];//在建立x的另一侧儿子与x父亲的联系 
    a[x].ch[!k]=y,a[y].ff=x; //重新联系x,y
    pushup(y),pushup(x);//因为y是x的儿子了,要先更新y 
}

将x旋转至d的儿子的位置(Splay灵魂)

旋转时要根据当前点与其父亲、爷爷的关系调整旋转顺序保证树高

in void Splay(int x,int d)
{
    while(a[x].ff!=d)
    {
        int y=a[x].ff;  
        int z=a[y].ff;
        if(z!=d)
            (check(x,y) ^ check(y,z) ? rotate(x) : rotate(y));
            //如果x,y,z在三点不是一条直线则先旋转x ,不然先旋y(这样可以保证复杂度) 
        rotate(x); // 肯定还要旋一次x 
    }
    if(d==0) root=x; //更新root 
}

然后就是一些其他运用,在完整代码中提到

完整代码

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define in inline
#define get getchar()
in int read()
{
    int t=0; char ch=get;
    while(ch<'0' || ch>'9') ch=get;
    while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
    return t;
}
const int _=1e5+4;
struct yzhx{
    int ch[2],cnt,siz,ff,w;
}a[_];
int n,root,tot;
#define check(x,y) (a[y].ch[1]==x)
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
#define pushup(x) a[x].siz=a[x].cnt+a[ls(x)].siz+a[rs(x)].siz
in void rotate(int x) //将x向上旋转一层 (更新的顺序很重要!!!) 
{
    int y=a[x].ff; //x的父亲 
    int z=a[y].ff; //x的爷爷 
    int k=check(x,y); //x与y的方向关系 
    a[z].ch[check(y,z)]=x,a[x].ff=z;//先处理x的爷爷 
    a[a[x].ch[!k]].ff=y,a[y].ch[k]=a[x].ch[!k];//在建立x的另一侧儿子与x父亲的联系 
    a[x].ch[!k]=y,a[y].ff=x; //重新联系x,y
    pushup(y),pushup(x);//因为y是x的儿子了,要先更新y 
}
in void Splay(int x,int d)
{
    while(a[x].ff!=d)
    {
        int y=a[x].ff;  
        int z=a[y].ff;
        if(z!=d)
            (check(x,y) ^ check(y,z) ? rotate(x) : rotate(y));
            //如果x,y,z在三点不是一条直线则先旋转x ,不然先旋y(这样可以保证复杂度) 
        rotate(x); // 肯定还要旋一次x 
    }
    if(d==0) root=x; //更新root 
}
in void insert(int x)
{
    int k=root,ff=0;
    while(k && a[k].w!=x) ff=k,k=a[k].ch[(a[k].w<x)];//找到待插入值的位置 ,并记录改点父亲 
    if(k) a[k].cnt++,a[k].siz++; //若已有之前该点 
    else //新建节点 
    {
        k=++tot;
        if(ff) a[ff].ch[a[ff].w<x]=k;
        a[k].cnt=a[k].siz=1;
        a[k].w=x,a[k].ff=ff;
    }
    Splay(k,0);
}
in void find(int x) //找到x(或是最接近x的值),并将其旋转至根
{
    int k=root;
    while(a[k].ch[a[k].w<x] && a[k].w!=x) k=a[k].ch[a[k].w<x];
    Splay(k,0);
    return;
}
in int kth(int x)//从小到大查询排名为x的数 
{
    int k=root;
    while(1)
    {
        if(a[ls(k)].siz>x) k=ls(k); //询问的点在当前点的左子树 
        else 
        {
            if(a[ls(k)].siz+a[x].cnt>=x) return k; //找到了 
            else x-=a[ls(k)].siz+a[x].cnt, k=rs(k); //询问的点在当前点的左子树
        }
    }
    return 0; 
}
in int Next(int x,int f)//f==1是找后继,f==0找前驱 
{
    find(x);
    int k=root;
    if(a[k].w>x && f)return k;
    if(a[k].w<x && !f) return k;
    k=a[k].ch[f]; //先走到左子树or右子树,确保整棵子树都满足要求 
    while(a[k].ch[1^f]) k=a[k].ch[1^f];  //找当前子树内最大 or最小的 
    return k;
}
in void del(int x)
{
    int l=Next(x,0),r=Next(x,1);
    Splay(l,0),Splay(r,l);//先找到前驱和后继,并旋转 
    int k=ls(r);//此时r的后继左子树一定只有一个点,就是x 
    if(a[k].cnt>1)
    {
        a[k].cnt--,a[k].siz--;
        Splay(k,0);
    }
    else a[r].ch[0]=0;
}
int main()
{
    insert(-(0x3f3f3f3f)),insert(0x3f3f3f3f);//插入inf与-inf 
    n=read();
    for(re int i=1;i<=n;i++) 
        insert(read());
}
posted @ 2020-06-19 10:16  yzhx  阅读(241)  评论(0编辑  收藏  举报