• 伸展树 Splay 模板


    学习Splay的时候参考了很多不同的资料,然而参考资料太杂的后果就是模板调出来一直都有问题,尤其是最后发现网上找的各种资料均有不同程度的错误。

    好在啃了几天之后终于算是啃下来了。

    Splay也算是平衡树的一种,但是跟AVL树、SBT不同的是,Splay并不是一直保持严格的平衡,因此在速度上可能要慢一些,但是统计学上仍能保证Splay具有O(logn)的均摊复杂度。

    Splay原生不需要附加任何空间,它的先天优势是其特有的Splay操作可以非常灵活地改变整棵树的结构形态,完成一般线段树、平衡树做不到的任务。

    • 基本操作-左右旋

    Splay的基本操作与其他平衡树相似,都是进行结点的左右旋转。与之前写的SBT相比,Splay除了左右儿子域以外还需要一个father域,这个是因为下面的Splay操作需要用到父节点来判断当前的形态。

    • 核心操作-splay

    Splay(x,y)的作用是将当前点x旋转到y的子结点上,一般写y=0时则将x旋转到整棵树的根。这个操作在splay的所有函数中基本上最后都要加上,简单的理解就是将常用的结点尽可能地上移到深度较浅的地方,据统计若去掉函数最后对操作结点的splay,则整个程序将会慢上数倍。

    splay基本的想法是根据当前结点父节点形态的不同,分成四种情况,逐步将结点旋转到根上。

    • 进阶操作

    插入、查找值、查找第k个等等...基本与普通的二叉查找树相似,但是记得在最后要加上splay,将使用过的结点旋转到根。

    特殊的删除操作:

    splay本身是一棵二叉搜索树,可以进行一般的删除操作。另外,二叉搜索树的性质配合splay的操作可以做到O(logn)动态删除一整个区间的结点,而一般的平衡树则只能一个一个删除。

    例如:删除大于l且小于r的数,先将序号小于l的最大结点伸展到根,序号大于r的最小的结点伸展到根的右子树,则此时,r的后继结点的左子树中就存放着从l到r的所有结点,此时只要把这棵子树整个断开即可。

    ............

    /* ***********************************************
    MYID    : Chen Fan
    LANG    : G++
    PROG    : Splay Tree
    ************************************************ */
    
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    #define MAXN 100000
    
    int sons[MAXN][2];
    int father[MAXN],size[MAXN],data[MAXN];
    int spt=0,spttail=0;
    
    void rotate(int x,int w) //rotate(node,0/1)
    {
        int y=father[x];
        sons[y][1-w]=sons[x][w];
        if (sons[x][w]) father[sons[x][w]]=y;
    
        father[x]=father[y];
        if (father[y])
        if (y==sons[father[y]][0]) sons[father[y]][0]=x;
        else sons[father[y]][1]=x;
    
        sons[x][w]=y;
        father[y]=x;
    
        size[x]=size[y];
        size[y]=size[sons[y][0]]+size[sons[y][1]]+1;
    }
    
    void splay(int x,int y) //splay(node,position)
    {
        if (!x) return ;
        while(father[x]!=y)
        {
            if (father[father[x]]==y)
                if (x==sons[father[x]][0]) rotate(x,1);
                else rotate(x,0);
            else 
                if (father[x]==sons[father[father[x]]][0])
                    if (x==sons[father[x]][0])
                    {
                        rotate(father[x],1);
                        rotate(x,1);
                    } else 
                    {
                        rotate(x,0);
                        rotate(x,1);
                    }
                else 
                    if (x==sons[father[x]][1])
                    {
                        rotate(father[x],0);
                        rotate(x,0);
                    } else 
                    {
                        rotate(x,1);
                        rotate(x,0);
                    }
        }
        if (!y) spt=x;
    }
    
    void search(int x,int w)
    {
        while(data[x]!=w)
        {
            if (w<data[x])
            {
                if (sons[x][0]) x=sons[x][0];
                else break;
            } else if (w>data[x])
            {
                if (sons[x][1]) x=sons[x][1];
                else break;
            }
        }
        splay(x,0);
    }
    
    void insert(int w) //insert(value)
    {
        spttail++;
        data[spttail]=w;
        size[spttail]=1;
        sons[spttail][0]=0;
        sons[spttail][1]=0;
        if (!spt)
        {
            father[spttail]=0;
            spt=spttail;
        } else 
        {
            int x=spt;
            while(1)
            {
                size[x]++;
                if (w<data[x])
                    if (sons[x][0]) x=sons[x][0];
                    else break;
                else 
                    if (sons[x][1]) x=sons[x][1];
                    else break;
            }
            father[spttail]=x;
            if (w<data[x]) sons[x][0]=spttail;
            else sons[x][1]=spttail;
            splay(spttail,0);
        }
    }
    
    void select(int x,int v) //select(root,k)
    {
        while(v!=size[sons[x][0]]+1)
        {
            if (v<=size[sons[x][0]]) x=sons[x][0];
            else 
            {
                v-=size[sons[x][0]]+1;
                x=sons[x][1];
            }
        }
        splay(x,0);
    }
    
    int succ(int t)
    {
        t=sons[t][1];
        while(sons[t][0]) t=sons[t][0];
        splay(t,0);
        return t;
    }
    
    void del(int x) //del(number)
    {
        splay(x,0);
    
        int y=sons[x][0];
        while(!sons[y][1])
        {
            y=sons[y][1];
        }
    
        int z=sons[x][1];
        while(!sons[z][0])
        {
            z=sons[z][0];
        }
    
        if (y+z==0)
        {
            spt=0;
            spttail=0;
            return ;
        }
    
        if (y)
        {
            splay(y,0);
            size[y]--;
        }
        if (z)
        {
            splay(z,y);
            sons[z][0]=0;
            size[z]--;
        }
    }
    
    int rank(int v) //rank(value)
    {
        search(spt,v);
        return size[sons[spt][0]]+1;
    }
    
    int main()
    {
        memset(father,0,sizeof(father));
        memset(size,0,sizeof(size));
        memset(sons,0,sizeof(sons));
        memset(data,0,sizeof(data));
        spt=0;
        spttail=0;
    
        return 0;
    }
  • 相关阅读:
    Java常用类库(二):Iterator迭代器和子范围视图
    Java常用类库(一) : Object 和日期类的简单使用
    MyBatis的逆向工程
    AdminLTE介绍和zTree的简单使用
    MyBatis分页组件--PageHelper
    SpringAop--系统日志简例
    Shiro
    Mysql(三):多表查询和存储程序
    MySql(二):常见的那些个约束
    正则表达式一些用法
  • 原文地址:https://www.cnblogs.com/jcf94/p/4333497.html
Copyright © 2020-2023  润新知