• 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=x,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<k]=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=x,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<k]=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 k=rs(k),x-=a[ls(k)].siz+a[x].cnt; //询问的点在当前点的左子树
            }
        }
        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);//此时x的后继左子树一定只有一个点,就是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());
    }
    
  • 相关阅读:
    20155302 课堂实践二
    20155302 课堂实践
    2017-2018-1 20155302 《信息安全系统设计基础》第6周学习总结
    2017-2018-1 20155302 《信息安全系统设计基础》第5周学习总结
    2017-2018-1 20155302 《信息安全系统设计基础》第四周学习总结
    2017-2018-1 20155301 《信息安全系统设计基础》第九周学习总结
    课下作业和课上作业
    2017-2018-1 20155301 《信息安全系统设计基础》第八周学习总结
    信息安全系统设计基础实验二
    信息安全系统设计基础第二次实验
  • 原文地址:https://www.cnblogs.com/yzhx/p/13161598.html
走看看 - 开发者的网上家园