zoukankan      html  css  js  c++  java
  • 浅谈BST中的Splay

    Ⅰ、预备知识

    (Splay),二叉查找树(BST,Binary Search Tree)的一种。
    其性质满足中序遍历是严格上升的,即左子树中所有数的键值<根节点的键值<右子树中所有数的键值,这一特点,一般支持一下几种操作:
    1、插入一个权值为v的数
    2、删除一个权值为v的数
    3、查询v数的排名
    4、查询排名为x的数
    5、求v的前驱
    6、求v的后继
    类似能实现这些操作的BST还有比如非旋treap、fhq_treap、SBT等,均有各自数据结构的特点(蒟蒻全都不会)
    不过,写旋转treap的都是sb

    Ⅱ、分析问题

    先让我们丢一道题出来

    题目描述

    您需要写一种数据结构,来维护一些数,其中需要提供以下操作:
    1、插入(x)
    2、删除(x)数(若有多个相同的数,应只删除一个)
    3、查询(x)数的排名(排名定义为比当前数小的数的个数(+1)。若有多个相同的数,应输出最小的排名)
    4、查询排名为(x)的数
    5、求(x)的前驱(前驱定义为小于(x),且最大的数)
    6、求(x)的后继(后继定义为大于(x),且最小的数)

    输入输出格式

    输入格式:

    第一行为(n),表示操作的个数,下面(n)行每行有两个数(opt)(x)(opt)表示操作的序号((1leq optleq6))

    输出格式:

    对于操作(3,4,5,6)每行输出一个数,表示对应答案

    输入输出样例

    输入样例#1:

    10
    1 106465
    4 1
    1 317721
    1 460929
    1 644985
    1 84185
    1 89851
    6 81968
    1 492737
    5 493598

    输出样例#1:

    106465
    84185
    492737

    说明:

    时空限制:(1000ms),(128M)
    的数据范围:(nleq100000)
    每个数的数据范围:([-10^7,10^7])
    先来随手扯一颗(Splay)

    它是长这样的↑,符合前面说的(Splay)的性质
    但是,在某些情况下,它可能会被卡成这样

    很明显可以看出,树的深度越大,时间复杂度越高
    为了应对友(wu)好(liang)出题人把你的树卡成链,(Splay)最具代表性的操作(rotate)(划重点)出现了,它可以将一个节点旋转到其父节点位置而不破坏平衡树性质
    如图

    这是一颗(Splay)树,我们暂且觉得它很丑(rotate)操作将(X)结点旋转至(Y)结点后,它是这样的

    旋转后(X)的深度上升了,并且我们会发现(Splay)的性质仍未改变,这就非常的(Nice)和玄妙。通过(rotate)操作,我们可以维护一颗(Splay)树的性质,将任何一个节点旋转到根;同时,我们将由左图到右图(Y)变为(X)的右儿子的操作称为右旋(zig)操作;由右图到左图(X)变为(Y)的左儿子的操作称为左旋(zag)操作

    (rotate)操作代码(以下注释以右旋操作为例说明):

    inline void rot(int u){
    	bool kd=son[fa[u]][0]==u;//1右旋,0左旋 
    	int gf=fa[fa[u]],f=fa[u],sn=son[u][kd];
    	son[f][!kd]=sn;
    	if(sn)
    		fa[sn]=f;
    	son[u][kd]=f;
    	fa[f]=u;
    	if(gf)
    		son[gf][f==son[gf][1]]=u;
    	fa[u]=gf;
    	updata(u);
    	updata(f);
    }
    inline void splay(int u,int target){
    	while(fa[u]!=target)
    		rot(u);
    	if(!target)
    		rt=u;
    }
    
    class Splay{
        private:
        int tot,rt,cnt[100010],val[100010],son[100010][2],fa[100010],sz[100010];
        public:
        inline void destroy(int u){
            val[u]=cnt[u]=son[u][0]=son[u][1]=sz[u]=fa[u]=0;
        }
        inline void updata(int u){
            sz[u]=sz[son[u][0]]+sz[son[u][1]]+cnt[u];
        }
        inline void rot(int u){
    	    bool kd=son[fa[u]][0]==u;//1右旋,0左旋 
    	    int gf=fa[fa[u]],f=fa[u],sn=son[u][kd];
    	    son[f][!kd]=sn;
    	    if(sn)
    	    	fa[sn]=f;
    	    son[u][kd]=f;
    	    fa[f]=u;
    	    if(gf)
    	    	son[gf][f==son[gf][1]]=u;
    	    fa[u]=gf;
    	    updata(u);
    	    updata(f);
        }
        inline void splay(int u,int target){
            while(fa[u]!=target)
                rot(u);
            if(!target)
                rt=u;
        }
        inline int search(int u,int v){
            while(val[u]!=v){
                if(val[u]>v)
                    if(!son[u][0])
                        break;
                    else
                        u=son[u][0];
                else
                    if(!son[u][1])
                        break;
                    else
                        u=son[u][1];
            }
            return u;
        }
        inline void ins(int v){
            if(!tot){
                val[++tot]=v;
                rt=cnt[1]=sz[1]=1;
                return ;
            }
            int u=search(rt,v);
            if(val[u]==v){
                cnt[u]++;
                sz[u]++;
                splay(u,0);
                return ;
            }
            val[++tot]=v;
            cnt[tot]=sz[tot]=1;
            fa[tot]=u;
            son[u][v>val[u]]=tot;
            splay(tot,0);
        }
        inline int get_most(int u,bool kd){
        	if(!kd)
        		return search(u,-2147483647);
        	else
        		return search(u,2147483647);
        }	
        inline int get_pre(int v){
            int u=search(rt,v);
            splay(u,0);
            if(val[u]<v)
                return u;
            return get_most(son[u][0],1);
        }
        inline int get_nxt(int v){
            int u=search(rt,v);
            splay(u,0);
            if(val[u]>v)
                return u;
            return get_most(son[u][1],0);
        }
        inline int kth_most(int u,int k,bool kd){
            while(u){
                if(k>sz[son[u][kd]]+cnt[u]){
                    k=k-(sz[son[u][kd]]+cnt[u]);
                    u=son[u][!kd];
                }else{
                    if(k<=sz[son[u][kd]]){
                        u=son[u][kd];
                    }else{
                        splay(u,0);
                        break;
                    }
                }
            }
            return u;
        }
        inline int get_rank(int v){
            int u=search(rt,v);
            splay(u,0);
            if(val[u]>=v)
                return sz[son[u][0]]+1;
            else
                return sz[son[u][0]]+cnt[u]+1;
        }
        inline void del(int v){
            int u=search(rt,v);
            if(val[u]!=v)
                return;
            if(--cnt[u]){
                splay(u,0);
                return;
            }else{
                splay(u,0);
                if(!son[u][0]){
                    fa[son[u][1]]=0;
                    rt=son[u][1];
                    destroy(u);
                    return ;
                }
    			if(!son[u][1]){
                    fa[son[u][0]]=0;
                    rt=son[u][0];
                    destroy(u);
                    return ;
    			}
                fa[son[u][0]]=0;
                splay(get_most(son[u][0],1),0);
                son[rt][1]=son[u][1];sz[rt]+=sz[son[u][1]];
                if(son[rt][1])
                    fa[son[rt][1]]=rt;
                destroy(u);
            }
        }
        inline int kth(int k,bool kd){
        	return val[kth_most(rt,k,kd)];
        }
        inline int pre(int v){
        	return val[get_pre(v)];
        }
        inline int nxt(int v){
        	return val[get_nxt(v)];
        }
    }S;
    
  • 相关阅读:
    spark internal
    74LS153 选择器 【数字电路】
    Linux(centos)下mysql编译安装教程
    Android画布更新过程OnDraw调用过程
    Android中使用ContentProvider进行跨进程方法调用
    hdu5414(2015多校10)--CRB and String(字符串匹配)
    徒弟们对话,遇到sb领导,离职吧
    【Cocos2d-x 017】 多分辨率适配全然解析
    编程精粹--编写高质量C语言代码(4):为子系统设防(一)
    为SSD编程(4)——高级功能和内部并行
  • 原文地址:https://www.cnblogs.com/hzf29721/p/10078912.html
Copyright © 2011-2022 走看看