• 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点权主键版
  • 相关阅读:
    JS中let和Var的区别
    Oracle拼接字符串
    C#System.Data.StrongTypingException:“无法将类型为System.DBNull的对象强制转换为类型System.String”
    C#System.ArgumentException:“该行已经属于另一个表“
    XML Scema出现提示无法找到对象“Settings”的链接“ConnectionStrings(Settings)1”,原因为:在应用程序。。。的解决方法
    Dev Express中GridControl控件显示多列checkBox
    Dev Express中GridControl控件时间显示精确到时分秒
    ORA-01745: 无效的主机/绑定变量名(补充说明其他的一些错误并附上保留字字典)
    vm虚拟机系类问题
    学手工分区- fdisk
  • 原文地址:https://www.cnblogs.com/DCD112358/p/9532682.html
Copyright © 2020-2023  润新知