zoukankan      html  css  js  c++  java
  • splay学习笔记

    开始前先放个卿学姐的视频链接:https://www.bilibili.com/video/av6648219?from=search&seid=2858898196423073520)

    对于平衡树我先设几个结构体与数组数值

    int root;             ///记录根节点
    int tot;              ///记录下一个节点要存在数组的第几位
    int fa[N];             ///记录这个节点的父亲值
    struct Node{
      int siz;           ///记录以这个节点为根的子树的大小
      int son[2];        ///记录这个节点左右儿子的位置 
      int val;           ///记录当前节点保存的权值
      void init(int w){  ///这个函数是为了快速初始化一个节点
        val=w;siz=1;
        son[0]=son[1]=0;
      }
    }T[N];                ///开N个节点
    

      


    旋转(单旋【左旋、右旋】、双旋):

      splay的核心就是旋转,通过旋转让整棵树能尽量保持在log级别的高度;

      只拿单旋来说明的话:一个节点在他父亲节点的左边就右旋,反之同样;(先别在意单旋是什么东西)

      zig(右旋):将x旋转至y

    》》》》》》》

        这个就是简单的右旋,你会发现我们要考虑的内容其实就是:

        1、将z的儿子x的父亲进行修改;

        2、对x节点、x节点的父亲进行修改;

        3、对y节点的父亲以及y节点的儿子进行修改;

         ps:这里加红的字会在下面左旋操作里进行文字说明并对比。

      zag(左旋):将x旋转至y

    》》》》》》》

        左旋的内容:

        1、将z的儿子x的父亲进行修改;

        2、对x节点、x节点的父亲进行修改;

        3、对y节点的父亲以及y节点的儿子进行修改;

        好的,和上面右旋相比是不是发现除了左右字眼,其他都没变!!!记住这个结论,这个是我们写Rotate函数(旋转操作)的重要依据,因为这个结论我们可以缩减大量的代码!

    ///这个是一个基础操作,先在这里贴出来
    void pushup(int x){////更新以这个点为根的树的size,就是左右加自己
        T[x].siz=1;
        if(T[x].son[0]){
            T[x].siz+=T[T[x].son[0]].siz;
        }
        if(T[x].son[1]){
            T[x].siz+=T[T[x].son[1]].siz;
        }
    }
    
    void Rotate(int x, int kind){///将x旋转 右旋为1
        int y=fa[x];
        int z=fa[y];
        T[y].son[!kind]=T[x].son[kind]; fa[T[x].son[kind]]=y;
        ///操作x点的b节点和y点的关系
        T[x].son[kind]=y; fa[y]=x;
        ///操作x点和y点的关系
        T[z].son[T[z].son[1]==y]=x; fa[x]=z;
        ///操作x点和z点的关系
        pushup(y);///看看上面的关系变动你会发现变动了大小的只有y和x,但是我们现在就更新个y,至于为什么,等一下splay函数里有解释
    }
    

      


      我们知道zig和zag的工作原理以后会浮现出一个问题、如下图(将e转到b,一次性画出结果,过程的话自己可以一步步模拟,加深对上面zig、zag的理解):

       》》》》》》》》》

      这个变换很明显的一个问题就是树的深度完全没变,这是为什么呢!!这里就涉及到了上面挖的坑了!!

      单旋:就是基础的zig、zag,也就是如果当前节点在父节点的左边,那么我就zig,反之亦然;

      双旋:每次旋转时候选3个点出来,x,x父亲(y),y的父亲(z);然后先将y旋转至z,再将x旋转到y

      下面还是对上面这个情况,我们用双旋来将它再旋一次、这次我会拆解第一次旋转:

      取出e,d,c,先旋d,c再旋e,d

      好家伙、完全没用,说好的降低深度呢!!!

      但其实这只不过是一个意外,第一次而已,后面还有几步!!让我们来直接得到结果图吧!!

    这个就是结果,树的高度确实变小了;这个地方的结论也要记住,这个是我们写splay函数(将x旋转至goal)的重要依据;

    void Splay(int x, int goal){    ///这里要再次声明,spaly是将x转到goal的子节点处
        if(x==goal) return ;
        while(fa[x]!=goal){
            int y=fa[x];
            int z=fa[y];
            int RX=T[y].son[0]==x;   ///记录x的操作是要左旋还是右旋,Fa的左节点等于x,那么就是x在Fa的左边,那么就要将x右旋才能向上跑,对吧!
            int RY=T[z].son[0]==y;    ///同理
            if(z==goal){         ///这个情况只要根据所得到的RX来旋转x就好了,因为我们在这个循环里的操作是为了将x旋转到goal的其中一个子节点
                Rotate(x, RX);
            }else{            ///要先转Fa点、因为是双旋
                if(RX==RY){       ///说明x和Fa点都在GrandFa的其中一边,且x也在Fa点的那一边, 假设都在左边,那么就是要把x右旋右旋操作到GrandFa
                    Rotate(y, RY);
                }else{          ///这里就是相当于x在Fa的左节点,但Fa在GrandFa的右节点,这个情况就要右旋左旋
                    Rotate(x, RX);
                }
                Rotate(x, RY);
            }
        }
        pushup(x);            ///因为我们会在Rotate的时候沿途更新和x交换的节点的siz,所以说我们现在只要更新x的size
        if(goal==0) root=x;
    }
    

    在这里我先甩下一句:Splay的核心就转转转来减少树的高度,没事转一转,大部分时候可以减少被卡时间的风险,比如找个数,找到顺便转一转,可以看下面的Kth操作和Find操作


    现在我们已经将splay的旋转部分都学完了;

    那么接下来我们就该开始建这颗树了,我们的insert操作很简单,就是将一个val值扔到函数里,比当前节点大就把他扔右边节点去,否则就扔左边,相等的情况看你的实际情况,要记录次数就记录,不然就直接结束;

    (当然,如果树是空的怎么办,那就将现在这个节点作为根节点插入!!!)

    好的,我们已经知道怎么插入了,但是很明显,我又要来说一下这个方案的缺点了!!!对于下面这个数据你们会怎么插入?num[ ]={1,2,3,4,5,6,7,8,9,10,,,,,,n}

    当然,我们先正常插入吧!第一个数直接插入为根,第二个数查一次后插入树里。。。。。。第n个数查询n-1次插入树里,,,,,,妈耶,时间复杂度是多少!!!O((n-1)*n/2),(#挠头)我还不如直接暴力算了!!

    那么为了解决这样的数据我们该怎么办呢!你会发现要是我最后一次插入的数存在根的话,复杂度会变成O(1e6),那么我们每次旋转当前插入的树为根,这样旋转最长的长度也才logn,均摊的话插入操作复杂度是O(nlogn);很棒!!那么又来了,记住这个结论,这个是我们写Insert函数(插入数据)的必备优化;

    void Insert(int &t, int val, int Pa=0){///int &t我们一般的传入参数是root,即Insert(root, val)[这里真的没有少写参数];   int Pa=0即不传入参数的情况下,Pa默认等于0
        if(t==0){///假设我们现在插入一个数,就应该是Insert(root, val, 0);这样书写,那么也就是说,如果我还没一个节点的时候,root肯定是0(因为初始化),那么就要直接插入,其他情况也要用到这个,我们下面再说
            t=++tot;///给新建节点一个位置;
            T[t].init(val);///初始化这个节点
            fa[t]=Pa;
            Splay(t, 0);///这个就是刚刚的那个优化技巧
        }else{
            Pa=t;
            if(val<T[t].val) Insert(T[t].son[0], val, Pa);///这里只将他下放到子节点,因为子节点初始化过,所以说肯定为0,也就是传入参数为0,然后就可以用到上面的那个新建节点的操作了
            else if(val>T[t].val) Insert(T[t].son[1], val, Pa);///这里这样写是为了不将相同的数插入,但是再加上一个else就可以实现将这个数字的出现次数++的操作了
            pushup(Pa);///更新一下树的节点大小
        }
    }
    

    我们既然已经可以建成一颗树了,那么我们就要用到平衡树的功能之第k大了!!

     很明显,我们会发现,对于一颗平衡树,我们常常写的是找第k小,实际意义是找出有多少个数比他小,也就是插入1、2、3,找k=0就是1,以此类推;

    也就是说,我们可以根据一点的左儿子的size(以这个节点为根节点的树的大小)来找有多少个数比当前的数小;我们根据代码来解释吧!

    int Kth(int k){                    ///现在是求第k小, Kth(T[root].siz-k)是求第k大
        int now=root;
        while(T[T[now].son[0]].siz!=k){///如果刚刚好有k个数比他小的话就直接是根节点
            if(k<T[T[now].son[0]].siz){///如果比这个节点小的数大于k个
                now=T[now].son[0];     ///继续向左子树移动
            }else{                     ///如果比这个节点小的数不足k个,那么我们就要往右边更大的数跑,但是我们发现,这个k其实包括了左子树的size和当前根节点,也就是其实我们已经和这些点确认过了,你不是我要找的点,但是如果继续往右跑,右子树的size肯定会变小,那么就把已经不可能回去的部分减去从右节点开始求新的k的第k大就好了
                k-=T[T[now].son[0]].siz+1;
                now=T[now].son[1];
            }
        }
        Splay(now, root);        ///这里其实和Insert里的优化一样,如果我无限访问一个最深的节点,那不是很浪费时间,于是就直接将他旋转上去,也就是没事转一转
        return T[now].val;
    }
    

     


    已经有了第k大的数的函数了,那我们就来写一下一个数是第几大的函数吧!首先,一个数传入,比当前节点大就去右子树找,并将当前节点转换为他的右节点,找到了这个节点就退出,然后将他旋转到根,看他的左子树有多少个节点来判断他的大小

    int Find(int now, int val, int &Rank){
        Rank=1;
        while(T[now].val!=val){
            if(T[now].val>val){
                if(T[now].son[0]){
                    now=T[now].son[0];
                }else{///没找到的操作
                    
                }
            }else if(T[now].val<val){
                if(T[now].son[1]){
                    now=T[now].son[1];
                }else{///没找到的操作
                    
                }
            }
        }
        Splay(now, 0);
        if(T[root].son[0]){
            Rank+=T[T[root].son[0]].siz;
        }
        return T[now].val;
    }
    

      


    接下来我们来讲讲splay的删除节点操作,这个操作是删除根节点,也就是如果要删除一个点你要找到(Find函数)这个节点的位置并将他旋转到根;

    我们来讨论一下根节点的性质:对于根节点,他左边没有一个节点比他大,他右边没有一个节点比他小;也就是如果要替换这个根,我们就要用左子树的最大值或者右子树的最小值去替换他;

    那删除节点的操作(用左子树的最大值替换根)就是找到(Find(root, val))节点k将他旋转为根(Splay(k, 0)),找到左子树的最大值(循环直接找)的节点x,将x旋转为k的子节(Splay(x, k)),模拟替换过程;下面看代码

    void Delete( ){
        if(!T[root].son[0]){///先判断有没有左子树,没有直接换根
            fa[T[root].son[1]]=0;
            root=T[root].son[1];
        }
        else{
            int now=T[root].son[0];
            while(T[now].son[1]) now=T[now].son[1];///左子树最大值的节点位置
            Splay(now, root);///将左子树最大值的节点转到根节点的儿子处
    
            /**下面就是模拟替换过程,因为now是左子树的最大值,而且他旋转到根节点处也只能变成左节点,因为他不可能比根大,所以说他是根的左儿子,但是因为他是左子树里最大的所以说他没有右子树,也就是此时我们就可以将now设为根节点,将原来根节点的右子树直接接上now的右节点**/
            T[now].son[1]=T[root].son[1];///将空的有节点接上根的左子树并删除根
            root=now,fa[now]=0,fa[T[root].son[1]]=root;///不理解就画个图模拟一下
            pushup(root);
        }
    }
    

    前驱和后继什么的,基本上就是找比这个数小/大的下一个数,那么我们可以用Find找val是第k大,再找k+1大是谁,或k-1大是谁来解决,我就不多写了!!!!

    以上就是以点权为主键的splay;



    接下来我们来讲讲以下标为主键的splay(就是支持区间操作的):

    看视频吧!!!!!卿学姐的视频里讲的就是这样的,那个找第k大的性质和上面讲的不太一样,你们只要知道,如果是求一堆数字的第k大的时候就用以点权为主键的splay,有区间操作就用以下标为主键的splay,当然,两个结合的情况。。。。抱歉,我菜,不会树套树。

    接下来就是重要的板子了:(下标版的主函数是用来写了序列终结者)

      1 #include<bits/stdc++.h>
      2 #define lson(x) T[x].son[0]
      3 #define rson(x) T[x].son[1]
      4 #define SIZE(tree) (tree.T[tree.root].siz)
      5 using namespace std;
      6 const int N=1e6+7, INF=0x3f3f3f3f;
      7 struct Splay_Tree{
      8     int root, tot, fa[N];
      9     struct T{
     10         int siz, son[2], val, lazy, maxn;
     11         int rev;
     12         void init(int v){
     13             val=maxn=v;siz=1;
     14             son[0]=son[1]=rev=lazy=0;
     15         }
     16     }T[N];
     17     void init(){
     18         tot=root=1;
     19         T[1].init(-INF);
     20     }
     21     void pushup(int x){///更新以这个点为根的树的size
     22         T[x].siz=1;
     23         T[x].maxn=T[x].val;
     24         if(lson(x)){
     25             T[x].maxn=max(T[T[x].son[0]].maxn, T[x].maxn);
     26             T[x].siz+=T[T[x].son[0]].siz;
     27         }
     28         if(rson(x)){
     29             T[x].maxn=max(T[rson(x)].maxn, T[x].maxn);
     30             T[x].siz+=T[rson(x)].siz;
     31         }
     32     }
     33     void pushdown(int x){
     34         if(x==0) return ;
     35         if(T[x].lazy){
     36             if(lson(x)){
     37                 T[lson(x)].val+=T[x].lazy;
     38                 T[lson(x)].maxn+=T[x].lazy;
     39                 T[lson(x)].lazy+=T[x].lazy;
     40             }
     41             if(rson(x)){
     42                 T[rson(x)].val+=T[x].lazy;
     43                 T[rson(x)].maxn+=T[x].lazy;
     44                 T[rson(x)].lazy+=T[x].lazy;
     45             }
     46             T[x].lazy=0;
     47         }
     48         if(T[x].rev){
     49             if(lson(x)) T[lson(x)].rev^=1;
     50             if(rson(x)) T[rson(x)].rev^=1;
     51             swap(lson(x), rson(x));
     52             T[x].rev=0;
     53         }
     54     }
     55     void Rotate(int x, int kind){///将x旋转 右旋为1
     56         int y=fa[x];
     57         int z=fa[y];
     58         T[y].son[!kind]=T[x].son[kind]; fa[T[x].son[kind]]=y;
     59         ///操作x点的B节点和y点的关系
     60         T[x].son[kind]=y; fa[y]=x;
     61         ///操作x点和y点的关系
     62         T[z].son[T[z].son[1]==y]=x; fa[x]=z;
     63         ///操作x点和z点的关系
     64         pushup(y);
     65     }
     66     void Splay(int x, int goal){///这里要声明,spaly是将x转到goal的子节点处
     67         if(x==goal) return ;
     68         while(fa[x]!=goal){
     69             int y=fa[x];
     70             int z=fa[y];
     71             pushdown(z), pushdown(y), pushdown(x);
     72             int RX=lson(y)==x;///记录x的操作是要左旋还是右旋,Fa的左节点等于x,那么就是x在Fa的左边,那么就要将x右旋才能向上跑,对吧!
     73             int RY=lson(z)==y;///同理
     74             if(z==goal){///这个情况只要根据所得到的RX来旋转x就好了,因为我们在这个循环里的操作是为了将x旋转到goal的其中一个子节点
     75                 Rotate(x, RX);
     76             }else{///解释为什么要先转Fa点
     77                 if(RX==RY){///说明x和Fa点都在GrandFa的其中一边,且x也在Fa点的那一边, 假设都在左边,那么就是要把x右旋右旋操作到GrandFa
     78                     Rotate(y, RY);
     79                 }else{///这里就是相当于x在Fa的左节点,但Fa在GrandFa的右节点,这个情况就要右旋左旋
     80                     Rotate(x, RX);
     81                 }
     82                 Rotate(x, RY);
     83             }
     84         }
     85         pushup(x);
     86         if(goal==0) root=x;
     87     }
     88     int Kth(int k){///现在是求第k小, Kth(SIZE-k)是求第k大
     89         int now=root;
     90         pushdown(now);
     91         while(T[lson(now)].siz!=k){///这里其实就是在找一个点,这个点满足有k-1个子节点
     92             if(k<T[lson(now)].siz){
     93                 now=lson(now);
     94             }else{
     95                 k-=T[lson(now)].siz+1;
     96                 now=rson(now);
     97             }
     98             pushdown(now);
     99         }
    100         Splay(now, root);
    101         return now;
    102     }
    103     void Insert(int &t, int val, int Pa=0){
    104         if(t==0){///假设我们现在插入一个数,就应该是Insert(root, val, 0);这样书写,那么也就是说,如果我还没一个节点的时候,root肯定是0,那么就要直接插入,其他情况也要用到这个,我们下面再说
    105             t=++tot;///给新建节点一个位置;
    106             T[t].init(val);
    107             fa[t]=Pa;
    108             Splay(t, 0);
    109         }else{
    110             Pa=t;
    111             if(val<T[t].val) Insert(lson(t), val, Pa);///这里只将他下放到子节点,因为子节点初始化过,所以说肯定为0,也就是传入参数为0,然后就可以用到上面的那个新建节点的操作了
    112             else if(val>T[t].val) Insert(rson(t), val, Pa);///这里这样写是为了不将相同的数插入,但是再加上一个else就可以实现将这个数字的出现次数++的操作了
    113             pushup(Pa);///更新一下树的节点大小
    114         }
    115     }
    116     void splay_lr(int L, int R, int &u, int &v){
    117         u=Kth(L-1), v=Kth(R+1);
    118         Splay(u, 0);Splay(v, u);
    119     }
    120     void update(int L, int R, int val){
    121         int u, v;
    122         splay_lr(L, R, u, v);
    123         T[lson(v)].maxn+=val;
    124         T[lson(v)].val+=val;
    125         T[lson(v)].lazy+=val;
    126     }
    127     void Rev(int L, int R){
    128         int u, v;
    129         splay_lr(L, R, u, v);
    130         T[lson(v)].rev^=1;
    131     }
    132     int query(int L, int R){
    133         int u, v;
    134         splay_lr(L, R, u, v);
    135         return T[lson(v)].maxn;
    136     }
    137     int build(int L, int R){
    138         if(L>R) return 0;
    139         if(L==R) return L;
    140         int mid=(L+R)>>1, sl, sr;
    141         lson(mid)=sl=build(L, mid-1);
    142         rson(mid)=sr=build(mid+1, R);
    143         fa[sl]=fa[sr]=mid;
    144         pushup(mid);
    145         return mid;
    146     }
    147     void init(int n){
    148         T[0].init(-INF), T[1].init(-INF), T[n+2].init(-INF);
    149         for(register int i=2; i<=n+1; ++i) T[i].init(0);
    150         root=build(1, n+2), fa[root]=0;
    151         fa[0]=0;T[0].son[1]=root;T[0].siz=0;
    152     }
    153 }tree;
    154 int main( ){
    155     int n, m, op, v, L, R;
    156     scanf("%d%d", &n, &m);
    157     tree.init(n);
    158     while(m--){
    159         scanf("%d%d%d", &op, &L, &R);
    160         if(op==1){
    161             scanf("%d", &v);
    162             tree.update(L, R, v);
    163         }
    164         if(op==2){
    165             tree.Rev(L, R);
    166         }
    167         if(op==3){
    168             printf("%d
    ", tree.query(L, R));
    169         }
    170     }
    171 }
    splay下标主键版
      1 #include<bits/stdc++.h>
      2 #define SIZE(tree) (tree.T[tree.root].siz)
      3 using namespace std;
      4 const int N=1e6+7, INF=0x3f3f3f3f;
      5 struct Splay_Tree{
      6     int root, tot, fa[N];
      7     struct T{
      8         int siz, son[2], val;
      9         void init(int w){
     10             val=w;siz=1;
     11             son[0]=son[1]=0;
     12         }
     13     }T[N];
     14     void init(){
     15         tot=root=1;
     16         T[1].init(-INF);
     17     }
     18     void pushup(int x){///更新以这个点为根的树的size
     19         T[x].siz=1;
     20         if(T[x].son[0]){
     21             T[x].siz+=T[T[x].son[0]].siz;
     22         }
     23         if(T[x].son[1]){
     24             T[x].siz+=T[T[x].son[1]].siz;
     25         }
     26     }
     27     void Rotate(int x, int kind){///将x旋转 右旋为1
     28         int y=fa[x];
     29         int z=fa[y];
     30         T[y].son[!kind]=T[x].son[kind]; fa[T[x].son[kind]]=y;
     31         ///操作x点的B节点和y点的关系
     32         T[x].son[kind]=y; fa[y]=x;
     33         ///操作x点和y点的关系
     34         T[z].son[T[z].son[1]==y]=x; fa[x]=z;
     35         ///操作x点和z点的关系
     36         pushup(y);
     37     }
     38     void Splay(int x, int goal){///这里要声明,spaly是将x转到goal的子节点处
     39         if(x==goal) return ;
     40         while(fa[x]!=goal){
     41             int y=fa[x];
     42             int z=fa[y];
     43             int RX=T[y].son[0]==x;///记录x的操作是要左旋还是右旋,Fa的左节点等于x,那么就是x在Fa的左边,那么就要将x右旋才能向上跑,对吧!
     44             int RY=T[z].son[0]==y;///同理
     45             if(z==goal){///这个情况只要根据所得到的RX来旋转x就好了,因为我们在这个循环里的操作是为了将x旋转到goal的其中一个子节点
     46                 Rotate(x, RX);
     47             }else{///解释为什么要先转Fa点
     48                 if(RX==RY){///说明x和Fa点都在GrandFa的其中一边,且x也在Fa点的那一边, 假设都在左边,那么就是要把x右旋右旋操作到GrandFa
     49                     Rotate(y, RY);
     50                 }else{///这里就是相当于x在Fa的左节点,但Fa在GrandFa的右节点,这个情况就要右旋左旋
     51                     Rotate(x, RX);
     52                 }
     53                 Rotate(x, RY);
     54             }
     55         }
     56         pushup(x);
     57         if(goal==0) root=x;
     58     }
     59     void Insert(int &t, int val, int Pa=0){
     60         if(t==0){///假设我们现在插入一个数,就应该是Insert(root, val, 0);这样书写,那么也就是说,如果我还没一个节点的时候,root肯定是0,那么就要直接插入,其他情况也要用到这个,我们下面再说
     61             t=++tot;///给新建节点一个位置;
     62             T[t].init(val);
     63             fa[t]=Pa;
     64             Splay(t, 0);
     65         }else{
     66             Pa=t;
     67             if(val<T[t].val) Insert(T[t].son[0], val, Pa);///这里只将他下放到子节点,因为子节点初始化过,所以说肯定为0,也就是传入参数为0,然后就可以用到上面的那个新建节点的操作了
     68             else if(val>T[t].val) Insert(T[t].son[1], val, Pa);///这里这样写是为了不将相同的数插入,但是再加上一个else就可以实现将这个数字的出现次数++的操作了
     69             pushup(Pa);///更新一下树的节点大小
     70         }
     71     }
     72     void Delete( ){
     73         if(!T[root].son[0]){///先判断有没有左子树,没有直接换根
     74             fa[T[root].son[1]]=0;
     75             root=T[root].son[1];
     76         }
     77         else{
     78             int now=T[root].son[0];
     79             while(T[now].son[1]) now=T[now].son[1];///左子树最大值的节点位置
     80             Splay(now, root);///将左子树最大值的节点转到根节点的儿子处
     81 
     82             /**下面就是模拟替换过程,因为now是左子树的最大值,而且他旋转到根节点处也只能变成左节点,因为他不可能比根大,所以说他是根的左儿子,但是因为他是左子树里最大的所以说他没有右子树,也就是此时我们就可以将now设为根节点,将原来根节点的右子树直接接上now的右节点**/
     83             T[now].son[1]=T[root].son[1];///将空的有节点接上根的左子树并删除根
     84             root=now,fa[now]=0,fa[T[root].son[1]]=root;///不理解就画个图模拟一下
     85             pushup(root);
     86         }
     87     }
     88     int Kth(int k){///现在是求第k小, Kth(SIZE-k)是求第k大
     89         int now=root;
     90         while(T[T[now].son[0]].siz!=k){///这里其实就是在找一个点,这个点满足有k-1个子节点
     91             if(k<T[T[now].son[0]].siz){
     92                 now=T[now].son[0];
     93             }else{
     94                 k-=T[T[now].son[0]].siz+1;
     95                 now=T[now].son[1];
     96             }
     97         }
     98         Splay(now, root);
     99         return T[now].val;
    100     }
    101     int Find(int now, int val){
    102         while(T[now].val!=val){
    103             if(T[now].val>val){
    104                 if(T[now].son[0]){
    105                     now=T[now].son[0];
    106                 }else{///没找到 具体要怎么弄返回值和操作依据题目
    107                     return -1;
    108                 }
    109             }else if(T[now].val<val){
    110                 if(T[now].son[1]){
    111                     now=T[now].son[1];
    112                 }else{///没找到
    113                     return -1;
    114                 }
    115             }
    116         }
    117         Splay(now, root);
    118         return now;
    119     }
    120 }TT;
    splay点权主键版
  • 相关阅读:
    Rotation Kinematics
    离职 mark
    PnP 问题方程怎么列?
    DSO windowed optimization 代码 (4)
    Adjoint of SE(3)
    IMU 预积分推导
    DSO windowed optimization 代码 (3)
    DSO windowed optimization 代码 (2)
    OKVIS 代码框架
    DSO windowed optimization 代码 (1)
  • 原文地址:https://www.cnblogs.com/DCD112358/p/9532682.html
Copyright © 2011-2022 走看看