• 模板【Splay Tree伸展树】


    PART1(算法思想简介)

    1.实现

    1.整个树是按照中序访问下标顺序不变来维护的(reverse操作变化了区间中各下标内存储的结点内容,但整棵树按照下标顺序访问依旧不变)

    2.难道不是splay时只要不断rotate(x)就行了嘛??

    图来源(最后一个图虽然稍有错误,但是大体上是对的)

     这样一字旋转的时候转动fa转动x交替进行,就不会出现转来转去还是一条链的情况了

    a.在有了伸展树后的第一个问题是如何表示任意一个【a,b】区间,显然只要用splay的操作将树根变为a-1,将树根的右节点变为b+1,则该节点的左子树即表示区间【a,b】

    b.只要我们在每个节点上记录关于以这个节点为根的子树的信息。还可以对区间进行整体修改,可以用和线段树类似的延迟标记技术,就是对于每个结点,再额外记录一个或多个标记,表示以这个结点为根的子树是否被进行了某种操作,并且这种操作影响其子结点的信息值。当然,既然记录了标记,那么旋转和其他一些操作中也就要相应地将标记向下传递。

    2.时间复杂度

    以后可以不断改进自己的代码,用这个题来测试数据

    3.特别优势

    a.用伸展树解决数列维护问题,可以支持两个线段树无法支持的操作:在某个位置插入一些数和删除一些连续的数。

    b.但是代码量很大,遇到问题还是应当首先考虑线段树,认真斟酌后再考虑splay tree

    4.适用情况

    5.需要注意的点

    这种题目并不适合将成员和函数封装成为一个树的结构体,为何?因为树的结构体只能从固定的root结点访问过来,但是这道题任何结点都可能成为访问操作的root结点。所以如果封装成一个结构体,大大减少了它的灵活性,对这个结构来说是十分不好的.....枯了

    // tree.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <string>
    #include <vector>
    #include <queue>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <ctime>
    #include <array>
    #include <set>
    using namespace std;
    vector<string> departString_string(string data)
    {
        vector<int> back_part;//output type
        int i, j;
        vector<string> part;
        string A_part;
        stringstream room;
        room.str(data);
        while (room >> A_part)
            part.push_back(A_part);
        return part;
    }
    //————————————————
    //版权声明:本文为CSDN博主「systemyff」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
    //原文链接:https ://blog.csdn.net/u014377763/article/details/113845555
    template<class ElemType>
    struct tree_point {
        ElemType data;//数据
        struct tree_point* l_child, * r_child;//左、右孩子指针
    };
    template<class ElemType>
    class BinaryTree {
    private:
        vector<tree_point<ElemType>*> outlist;
        tree_point<ElemType>* root;   // 头指针
    public:
        BinaryTree() :root(NULL)
        {
            //无参数的构造函数
        }
        ~BinaryTree()
        {
            //析构函数
        }
        void BinaryTree_fron(vector<ElemType> lis, ElemType nut)
        {
            stack<tree_point<ElemType>*> s;
            tree_point<ElemType>* p_Parent = NULL, * p_Child = NULL;
            int i = 0;
            int flag = 0;//控制左右
            p_Parent = new tree_point<ElemType>;
            p_Parent->data = lis[i];
            p_Parent->l_child = p_Parent->r_child = NULL;
            s.push(p_Parent);
            root = p_Parent;
            i = 1;
            flag = 0;
            while (!s.empty())
            {
                if (lis[i] != nut)
                {
                    p_Parent = new tree_point<ElemType>;
                    p_Parent->data = lis[i];
                    p_Parent->l_child = p_Parent->r_child = NULL;
                    if (flag == 0)
                    {
                        p_Child = s.top();
                        p_Child->l_child = p_Parent;
                    }
                    else if (flag == 1)
                    {
                        p_Child = s.top();
                        s.pop();
                        p_Child->r_child = p_Parent;
                    }
                    s.push(p_Parent);
                    flag = 0;
                }
                else
                {
                    if (flag == 0)
                        flag = 1;
                    else if (flag == 1)
                        s.pop();
                }
                i++;
            }
        }
     
        tree_point<ElemType>* get_root()
        {
            return root;
        }
        void qianxu(tree_point<ElemType>* t)
        {
            outlist.push_back(t);
            if (t->l_child != NULL)
                qianxu(t->l_child);
            if (t->r_child != NULL)
                qianxu(t->r_child);
            return;
        }
        void zhongxu(tree_point<ElemType>* t)
        {
            if (t->l_child != NULL)
                zhongxu(t->l_child);
            outlist.push_back(t);
            if (t->r_child != NULL)
                zhongxu(t->r_child);
            return;
        }
        void houxu(tree_point<ElemType>* t)
        {
            if (t->l_child != NULL)
                houxu(t->l_child);
            if (t->r_child != NULL)
                houxu(t->r_child);
            outlist.push_back(t);
            return;
        }
        int c_t = 0;
        void cengxu(tree_point<ElemType>* t)
        {
            vector<tree_point<ElemType>* > q;
            q.push_back(t);
            int last = 1, cur = 0;
            while (cur < q.size())
            {
                last = q.size();
                while (cur < last)
                {
                    outlist.push_back(q[cur]);
                    if (q[cur]->l_child)
                        q.push_back(q[cur]->l_child);
                    if (q[cur]->r_child)
                        q.push_back(q[cur]->r_child);
                    ++cur;
                }
                c_t++;
            }
        }
        void out_lis()
        {
            int i;
            for (i = 0; i < outlist.size(); i++)
            {
                cout << outlist[i]->data;
                if (i != outlist.size() - 1)
                    cout << ",";
                else
                    cout << endl;
            }
            outlist.clear();
        }
        int cnt = 0, p_cnt = 0;
        void t_cnt(tree_point<ElemType>* t)
        {
            p_cnt++;
            if (t->l_child != NULL && t->r_child != NULL)
                cnt++;
            if (t->l_child != NULL)
                t_cnt(t->l_child);
            if (t->r_child != NULL)
                t_cnt(t->r_child);
        }
        int cengshu()
        {
            c_t = 0;
            cengxu(root);
            outlist.clear();
            return c_t;
        }
        int P_cengshu(tree_point<ElemType>* t)
        {
            c_t = 0;
            cengxu(t);
            outlist.clear();
            return c_t;
        }
        int twocnt()
        {
            cnt = 0;
            t_cnt(root);
            return cnt;
        }
        int P_twocnt(tree_point<ElemType>* t)
        {
            cnt = 0;
            t_cnt(t);
            return cnt;
        }
        int pointcnt()
        {
            p_cnt = 0;
            t_cnt(root);
            return p_cnt;
        }
        tree_point<ElemType>* father_p;
        void find_fatherNow(tree_point<ElemType>* t, ElemType ss)
        {
            if (t->l_child != NULL)
                if (t->l_child->data == ss)
                {
                    father_p = t;
                    return;
                }
            if (t->r_child != NULL)
                if (t->r_child->data == ss)
                {
                    father_p = t;
                    return;
                }
            if (t->l_child != NULL)
                find_fatherNow(t->l_child, ss);
            if (t->r_child != NULL)
                find_fatherNow(t->r_child, ss);
            return ;
        }
        tree_point<ElemType>* find_father(tree_point<ElemType>* t, ElemType ss)
        {
            father_p = NULL;
            find_fatherNow(t, ss);
            return father_p;
        }
        tree_point<ElemType>* nas_p;
        void find_selfNOW(tree_point<ElemType>* t, ElemType ss)
        {
            if (t->data == ss)
                nas_p = t;
            if (t->l_child != NULL)
                find_selfNOW(t->l_child, ss);
            if (t->r_child != NULL)
                find_selfNOW(t->r_child, ss);
            return ;
        }
        tree_point<ElemType>* find_self(tree_point<ElemType>* t, ElemType ss)
        {
            nas_p = NULL;
            find_selfNOW(t, ss);
            return nas_p;
        }
        void fast_delChild(tree_point<ElemType>* t,bool flag)
        {
            if (flag == 0)
                t->l_child = NULL;
            else
                t->r_child = NULL;
            return;
        }
        void slow_delChild(tree_point<ElemType>* t, bool flag)
        {
            if (flag == 0)
                t->l_child = NULL;
            else
                t->r_child = NULL;
            return;
        }
    };
     
    int main()
    {
        string s, ins, nulls;
        vector<string> part_in;
        BinaryTree<string> a;
        cin >> nulls;
        cin.get();
        getline(cin, ins);
        part_in = departString_string(ins);
        string found;
        cin >> found;
        bool flag;
        cin >> flag;
        //======================================
        /*for (auto i : part_in)
        {
            cout << i << endl;
        }
        cout << part_in.size() << endl;
        cout << nulls << endl;*/
        //======================================
     
        a.BinaryTree_fron(part_in, nulls);
        /*a.qianxu(a.get_root());
        a.out_lis();
        a.zhongxu(a.get_root());
        a.out_lis();
        a.houxu(a.get_root());
        a.out_lis();
        cout << endl;*/
        //cout<<a.pointcnt()<<endl;
        /*a.cengxu(a.get_root());
        a.out_lis();*/
        //cout << a.cengshu() << endl;
        tree_point<string>* ans = a.find_father(a.get_root(), found);
        if (ans == NULL)
            cout << "NULL" << endl;
        else
        {
            if (flag == 0)// left child
            {
                if (ans->l_child == NULL || ans->l_child->data==found)
                    cout << "NULL" << endl;
                else
                {
                    cout << ans->l_child->data << endl;
                }
            }
            else
            {
                if (ans->r_child == NULL || ans->r_child->data == found)
                    cout << "NULL" << endl;
                else
                {
                    cout << ans->r_child->data<< endl;
                }
            }
        }
        //cout << a.P_cengshu(ans) << endl;
        return 0;
    }
    来看看便于封装的情况

     一个大佬还是把它用类套着结构体的方式封装了起来

    6.函数、变量名的解释+英文

    rotate(v.旋转,转动;轮班;轮换)

    7.dalao分析

    基础详解+代码(这个大佬的代码实测已经不太对劲了)

    喜新厌旧这个大佬代码简洁,思路清晰,很对我胃口

     基础模板版本

    下面代码是针对这个题了

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<cstring>
    #include<string>
    #include<vector>
    #include<queue>
    #include<iomanip>
    #include<iostream>
    #include<stack>
    using namespace std;
    
    ///-------------------------------------------------------常用规定部分
    //----------------------便于更改
    //nodeType(1~1e7) 都是int就不必麻烦了
    #define valueType_ int//valueType(1~1e19)
    //----------------------通用
    #define inf_ 0x3f3f3f3f//这是给int的inf,值为1061109567,2^31-1为2147483647
    #define reg_ register int
    #define for_(i, n) for(int i = 1; i <= n; i++)
    #define forReg_(i, n) for(reg_ i = 1; i <= n; i++)
    #define ll_ long long
    #define ull_ unsigned long long
    //----------------------边访问
    #define visitEdge_ int i = p[u]; ~i; i = e[i].next
    #define defineV_ int v = e[i].v
    #define defineVAvoidFa_ int v = e[i].v;if(v == fa) continue
    //----------------------线段树
    #define mid_ int mid = (l+r)>>1//mid的定义
    #define len_ (r-l+1)
    #define lId_ id<<1
    #define rId_ id<<1|1
    #define lSon_ id<<1,l,mid
    #define rSon_ id<<1|1,mid+1,r
    #define include_(x,y,l,r) x<=l && r<=y
    //----------------------其它
    const int maxN_ = 1e6+10;
    const int maxM_ = 1e4+10;
    ///-------------------------------------------------------变量声明部分
    //--------------------------------------其它
    struct SPLAY_NODE
    {
        int fa, son[2];
        int size, cnt, tag;//size:子树大小,cnt:该大小节点的个数,tag:表示以x为根的树要不要reverse
        valueType_ value;
    } sNodeg[maxN_];
    int original[maxN_],root,nodeNum;//original:原始数据,nodeNum:the number of sNodeg by now(动态改变的)
    ///--------------------------------------------------------函数声明部分
    //--------------------------------------其它
    inline bool Which(int x); //x是父亲的右子树吗
    inline void UpDate(int x);
    inline void PushDown(int x);
    int BuildSplayTree(int l, int r, int fa);
    inline void Rotate(int x);
    inline void Splay(int x,int goal);
    inline int Find(int order);//找到中序遍历第order个数
    inline void Reverse(int x,int y);
    inline void SplayDfs(int now);
    ///--------------------------------------------------------main函数部分
    int main()
    {
        //freopen("in.txt","r", stdin);
        //freopen("out.txt","w", stdout);
        ios::sync_with_stdio(false);
        //InitEdge();
        int n, m, x, y;
        cin >> n >> m;
        original[1] = -inf_, original[n+2] = inf_;
        for_(i, n)
        original[i+1] = i;
        root = BuildSplayTree(1,n+2,0);//有一个良好的定义变量习惯很重要……重复定义同一个变量(比如全局的和局部的同名)那么就会发生覆盖。
        for_(i, m)
        {
            cin >> x >> y;
            Reverse(x+1, y+1);
        }
        SplayDfs(root);
        return 0;
    }
    ///--------------------------------------------------------函数定义部分
    //----------------------------------其它
    inline bool Which(int x) //x是父亲的右子树吗
    {
        return x == sNodeg[sNodeg[x].fa].son[1];
    }
    //作用:如果这是非0(相当于NULL),就由两个子树的size更新x的size
    //要求:需要在之前保证两个子树值的正确
    inline void UpDate(int x)
    {
        if(x)
        {
            sNodeg[x].size = sNodeg[x].cnt;
            if(sNodeg[x].son[0])
                sNodeg[x].size += sNodeg[sNodeg[x].son[0]].size;
            if(sNodeg[x].son[1])
                sNodeg[x].size += sNodeg[sNodeg[x].son[1]].size;
        }
    }
    //解释:lazy数组存在的最上层的Node的结点本来就保证内容正确,但他下面的子树无法保证正确
    //作用:将x没有传递下去的操作传递给两个子树,使得子树的数据正确
    inline void PushDown(int x)
    {
        if(x && sNodeg[x].tag)
        {
            sNodeg[sNodeg[x].son[1]].tag ^= 1;
            sNodeg[sNodeg[x].son[0]].tag ^= 1;
            swap(sNodeg[x].son[1],sNodeg[x].son[0]);
            sNodeg[x].tag=0;
        }
    }
    //作用:按照原始数据的下标(而非权值大小)来建立slayTree(这样splayTree的结构就和下标挂钩了)
    //解释:如果想要tree结构和大小挂钩,就应当按照大小拍了序才build(本质还是结构和下标挂钩)
    int BuildSplayTree(int l, int r, int fa)
    {
        if(l > r)
            return 0;
        mid_;
        int x = ++nodeNum;
        sNodeg[x].fa = fa;
        sNodeg[x].cnt = 1;
        sNodeg[x].value = original[mid];
        sNodeg[x].son[0] = BuildSplayTree(l, mid - 1, x);
        sNodeg[x].son[1] = BuildSplayTree(mid + 1, r, x);
        UpDate(x);
        return x;
    }
    //作用:x通过左旋或者右旋顶替父亲结点(并且如果父亲结点是根的话要更新root)
    inline void Rotate(int x)
    {
        int fa = sNodeg[x].fa;
        int ffa = sNodeg[fa].fa;
        PushDown(fa), PushDown(x);//因为fa在上面,所以先传递fa
        bool w = Which(x);//w==1是fa的右子树
        sNodeg[fa].son[w] = sNodeg[x].son[w^1];
        if(sNodeg[x].son[w^1])
            sNodeg[sNodeg[fa].son[w]].fa=fa;
        sNodeg[fa].fa=x;
        sNodeg[x].fa=ffa;
        sNodeg[x].son[w^1]=fa;
        if(ffa)
        {
            sNodeg[ffa].son[sNodeg[ffa].son[1]==fa]=x;
        }
        UpDate(fa);//维护fa
        if(fa == root)
            root = x;
    }
    //作用:将x上升到父亲结点是goal
    inline void Splay(int x,int goal)
    {
        int fa = sNodeg[x].fa;
        int ffa = sNodeg[fa].fa;
        for(PushDown(x); fa != goal;fa = sNodeg[x].fa,ffa = sNodeg[fa].fa)//我觉得这里的pushDown是没用的,情况一rotatex自己会更新,情况而rotate(y)不影响x,rotate(x)自己也会更新,不rotate的时候,没必要下放rotate(不过如果是求和lazy另当别论,毕竟后面还要update(x)的,对!就是为了后面的update)
        {//不要忘记每次更新一下fa和ffa
            if(ffa != goal)//如果需要转移两次及以上
                (Which(x)^Which(fa))?Rotate(x):Rotate(fa);//位异或(^),不同才为1,即之字形转x,一字型转fa
            Rotate(x);
        }
        UpDate(x);
    }
    //作用:找到中序遍历第order个数
    inline int Find(int order)
    {
        int x = root;
        while(1)
        {
            PushDown(x);
            if(order <= sNodeg[sNodeg[x].son[0]].size)
                x = sNodeg[x].son[0];
            else
            {
                order -= sNodeg[sNodeg[x].son[0]].size + 1;
                if(!order) return x;//当根节点为要求点时就在此处(还未进入右子树)退出
                x = sNodeg[x].son[1];
            }
        }
    }
    //作用:颠倒中序遍历的[x,y]区间的数
    inline void Reverse(int x,int y)
    {
        int l = x-1, r = y+1;
        l = Find(l), r = Find(r);
        //解释:由于l肯定小于r,所以目标区间肯定是root(l)的右子树(r)的左子树
        Splay(l, 0);
        Splay(r, l);
        int pos = sNodeg[root].son[1];
        pos = sNodeg[pos].son[0];
        sNodeg[pos].tag ^= 1;
    }
    //作用:中序遍历展示value
    inline void SplayDfs(int now)
    {
        PushDown(now);
        if(sNodeg[now].son[0])
            SplayDfs(sNodeg[now].son[0]);
        if(sNodeg[now].value!=-inf_ && sNodeg[now].value!=inf_)
            cout << sNodeg[now].value << " ";
        if(sNodeg[now].son[1])
            SplayDfs(sNodeg[now].son[1]);
    }
    View Code

     下面代码是针对这个题了

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <ctime>
    #include <iostream>
    using namespace std;
    
    //nodeType(1~1e7) 都是int就不必麻烦了
    #define valueType_ int//valueType(1~1e19)
    //----------------------通用
    #define inf_ 2000000000//这是给int的inf,值为1061109567,2^31-1为2147483647
    #define reg_ register int
    #define for_(i, n) for(int i = 1; i <= n; i++)
    #define forReg_(i, n) for(reg_ i = 1; i <= n; i++)
    #define ll_ long long
    #define ull_ unsigned long long
    //----------------------边访问
    #define visitEdge_ int i = p[u]; ~i; i = e[i].next
    #define defineV_ int v = e[i].v
    #define defineVAvoidFa_ int v = e[i].v;if(v == fa) continue
    //----------------------线段树
    #define mid_ int mid = (l+r)>>1//mid的定义
    #define len_ (r-l+1)
    #define lId_ id<<1
    #define rId_ id<<1|1
    #define lSon_ id<<1,l,mid
    #define rSon_ id<<1|1,mid+1,r
    #define include_(x,y,l,r) x<=l && r<=y
    //----------------------其它
    const int maxN_ = 200007;
    const int maxM_ = 1e4+10;
    ///-------------------------------------------------------变量声明部分
    //--------------------------------------其它
    int val[maxN_], n, m;
    
    struct SPLAY_TREE
    {
    
    #define lson s[x].son[0]
    #define rson s[x].son[1]
        struct data
        {
            int val, siz, son[2], fa, cnt;
        } s[maxN_ * 10];
        int tot, root;
        inline int NewNode()
        {
            s[++tot].cnt = 1;
            return tot;
        }
        inline int Which(int x)
        {
            return s[s[x].fa].son[1] == x;
        }
        inline void PushUp(int x)
        {
            s[x].siz = s[lson].siz + s[rson].siz + s[x].cnt;
        }
    
        inline void Rotate(int x)
        {
            int fa = s[x].fa, ffa = s[fa].fa, op = Which(x);
            s[fa].son[op] = s[x].son[op ^ 1];
            if (s[fa].son[op])
                s[s[fa].son[op]].fa = fa;
            s[x].son[op ^ 1] = fa, s[fa].fa = x, s[x].fa = ffa;
            if (ffa)
                s[ffa].son[s[ffa].son[1] == fa] = x;
            PushUp(fa), PushUp(x);
        }
        void Splay(int x, int &tar)
        {
            for (int u = s[tar].fa, fa; (fa = s[x].fa) != u; Rotate(x))
                if (s[fa].fa != u)
                    Rotate(Which(fa) == Which(x) ? fa : x);
            tar = x;
        }
        void Build(int l, int r, int &x)
        {
            x = NewNode();
            int mid = (l + r) >> 1;
            s[x].val = val[mid];
            if (mid > l)
                Build(l, mid - 1, lson), s[lson].fa = x;
            if (r > mid)
                Build(mid + 1, r, rson), s[rson].fa = x;
            PushUp(x);
        }
        void Insert(int &x, int fa, int val)
        {
    
            if (!x)
                x = NewNode(), s[x].fa = fa, s[x].val = val;
            else if(s[x].val == val)
                s[x].cnt++;
            else
                Insert(s[x].son[val > s[x].val], x, val);
            PushUp(x);
        }
        int Find(int x, int val)
        {
            if (s[x].val == val)
                return x;
            else
                return Find(s[x].son[val > s[x].val], val);
        }
        int GetPre(int x, int val)
        {
            if (!x)
                return 0;
            if (s[x].val < val)
            {
                int a = x, b = GetPre(rson, val);
                return b == 0 ? a : b;
            }
            else
                return GetPre(lson, val);
        }
        int GetNext(int x, int val)
        {
            if (!x)
                return 0;
            if (s[x].val > val)
            {
                int a = x, b = GetNext(lson, val);
                return b == 0 ? a : b;
            }
            else
                return GetNext(rson, val);
        }
        void Delete(int val)
        {
            int x = Find(root, val), l, r;
            if(x && s[x].cnt > 1)
            {
                s[x].cnt--;
                Splay(x, root);
                return ;
            }
            Splay(x, root), l = lson, r = rson;
            while (s[l].son[1])
                l = s[l].son[1];
            Splay(l, s[x].son[0]);
            s[r].fa = l, s[l].fa = 0, s[l].son[1] = r, PushUp(l), root = l;
        }
        int Getrank(int val)
        {
            int x = GetPre(root, val);
            Splay(x, root);
            return s[lson].siz+s[x].cnt;
        }
        int Getnum(int x, int kth)
        {
            if (kth <= s[lson].siz)
                return Getnum(lson, kth);
            else if(kth > s[lson].siz + s[x].cnt)
                return Getnum(rson, kth - s[lson].siz - s[x].cnt);
            else
                return x;
    
        }
        void Print1ToTot()
        {
            cout <<"tot"<< tot<<endl;
            for(int i = 1; i <= tot; i++)
            {
                cout <<i<<"'s  value:"<< s[i].val <<" cnt: "<<s[i].cnt<<" sum: "<<s[i].siz<<endl;
            }
            cout << endl;
        }
        void PrintfMidDfs(int x)
        {
            if(!x)
                return ;
            PrintfMidDfs(s[x].son[0]);
            cout <<s[x].val<<endl;
            PrintfMidDfs(s[x].son[1]);
        }
    } bst;
    ///--------------------------------------------------------函数声明部分
    //--------------------------------------其它
    
    ///--------------------------------------------------------main函数部分
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i)
            scanf("%d", &val[i]);
        sort(val + 1, val + 1 + n);
        val[0] = -inf_, val[1 + n] = inf_;
        bst.Build(0, 1 + n, bst.root);
        int lastans = 0, op, x, y, ans = 0;
        for (int i = 1; i <= m; ++i)
        {
            scanf("%d%d", &op, &x);
            x ^= lastans, y = 0;
            if (op == 1)
                bst.Insert(bst.root, 0, x), y = bst.tot;
            if (op == 2)
                bst.Delete(x);
            if (op == 3)
                lastans = bst.Getrank(x), ans ^= lastans;
            if (op == 4)
                y = bst.Getnum(bst.root, x + 1), lastans = bst.s[y].val, ans ^= lastans;
            if (op == 5)
                y = bst.GetPre(bst.root, x), lastans = bst.s[y].val, ans ^= lastans;
            if (op == 6)
                y = bst.GetNext(bst.root, x), lastans = bst.s[y].val, ans ^= lastans;
            ///这一步可以加上
    
            if (y && (i % 10 == 0)) bst.Splay(y, bst.root);
    
        }
        printf("%d
    ", ans);
        return 0;
    }
    ///--------------------------------------------------------函数定义部分
    //----------------------------------其它
    View Code
    /**
    函数不只可以返回一个值,它可以通过函数的参数列表的应用返回一堆值(见search函数的parent的的用法,可对比root的用法)
    **/
    /**
    不仅可以通过递归访问树,也可以通过非递归(while)啊!这样节省了代码和思考的经历,妙!
    **/
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<cstring>
    #include<string>
    #include<vector>
    #include<queue>
    #include<iomanip>
    #include<iostream>
    #include<stack>
    using namespace std;
    
    ///-------------------------------------------------------常用规定部分
    //----------------------便于更改
    //nodeType(1~1e7) 都是int就不必麻烦了
    #define valueType_ int//valueType(1~1e19)
    //----------------------通用
    #define inf_ 0x3f3f3f3f//这是给int的inf,值为1061109567,2^31-1为2147483647
    #define reg_ register int
    #define for_(i, n) for(int i = 1; i <= n; i++)
    #define forReg_(i, n) for(reg_ i = 1; i <= n; i++)
    #define ll_ long long
    #define ull_ unsigned long long
    //----------------------边访问
    #define visitEdge_ int i = p[u]; ~i; i = e[i].next
    #define defineV_ int v = e[i].v
    #define defineVAvoidFa_ int v = e[i].v;if(v == fa) continue
    //----------------------线段树
    #define mid_ int mid = (l+r)>>1//mid的定义
    #define len_ (r-l+1)
    #define lId_ id<<1
    #define rId_ id<<1|1
    #define lSon_ id<<1,l,mid
    #define rSon_ id<<1|1,mid+1,r
    #define include_(x,y,l,r) x<=l && r<=y
    //----------------------其它
    const int maxN_ = 1e7+10;
    const int maxM_ = 1e4+10;
    ///-------------------------------------------------------变量声明部分
    //--------------------------------------其它
    struct SPLAY_NODE
    {
        int fa, son[2];
        int size, cnt, tag;//size:子树大小,cnt:该大小节点的个数,tag:表示以x为根的树要不要reverse
        valueType_ value;
    } sNodeg[maxN_];
    int originalg[maxN_],rootg,nodeNumg;//original:原始数据,nodeNum:the number of sNodeg by now(动态改变的)有效值[1,n]
    ///--------------------------------------------------------函数声明部分
    //--------------------------------------其它
    inline bool Which(int x); //x是父亲的右子树吗
    inline void UpDate(int x);
    inline void PushDown(int x);
    int BuildSplayTree(int l, int r, int fa);
    inline void Rotate(int x);
    inline void Splay(int x,int goal);
    inline int FindOder(int order);//找到中序遍历第order个数
    inline void Reverse(int x,int y);
    inline void SplayDfs(int now);
    int SearchValue(int x, valueType_ value, int &fa);
    bool Search(int x, int value);
    inline int CreatSplayNode(int value = 0);
    inline int SplayInsert(valueType_ value);
    inline int NextHalf(int x,int op);
    inline int Next(int value,int op);
    bool Delete(valueType_ value);
    int CountOder(int value);
    int FindKthNumber(int k);
    inline valueType_ Read()
    {
        valueType_ x=0;
        char c=getchar();
        while(c<'0'||c>'9')
            c=getchar();
        while(c>='0'&&c<='9')
        {
            x=(x<<1)+(x<<3)+c-'0';
            c=getchar();
        }
        return x;
    }
    ///--------------------------------------------------------main函数部分
    int main()
    {
        //freopen("in.txt","r", stdin);
        //freopen("out.txt","w", stdout);
        //ios::sync_with_stdio(false);
        //InitEdge();
        int n, m, num;
        n = Read();
        m =  Read();
        SplayInsert(-inf_);
        SplayInsert(inf_);
    
        for(int i = 1; i <= n; i++)
        {
            num  = Read();
            SplayInsert(num);
        }
        int ans = 0, last = 0;
        while(m--)
        {
            int opt, x;
            opt = Read();
            x  = Read();
            if (opt==1)
                SplayInsert(x);
            if (opt==2)
                Delete(x);
            if (opt==3)
                x = x^last,last = CountOder(x),ans ^= last;
                //printf("%d
    ",CountOder(x));
            if (opt==4)
                x = x^last,last = FindKthNumber(x+1), ans ^= last;
                //printf("%d
    ",FindKthNumber(x+1));//注意还有-inf呢
            if (opt==5)
                x = x^last,last = sNodeg[Next(x, 0)].value, ans ^= last;
                //printf("%d
    ",sNodeg[Next(x, 0)].value);
            if (opt==6)
                x = x^last,last = sNodeg[Next(x,1)].value, ans ^= last;
                //printf("%d
    ",sNodeg[Next(x,1)].value);
        }
        printf("%d
    ", ans);
        return 0;
    }
    ///--------------------------------------------------------函数定义部分
    //----------------------------------其它
    inline bool Which(int x) //x是父亲的右子树吗
    {
        return x == sNodeg[sNodeg[x].fa].son[1];
    }
    //作用:如果这是非0(相当于NULL),就由两个子树的size更新x的size
    //要求:需要在之前保证两个子树值的正确
    inline void UpDate(int x)
    {
        if(x)
        {
            sNodeg[x].size = sNodeg[x].cnt;
            if(sNodeg[x].son[0])
                sNodeg[x].size += sNodeg[sNodeg[x].son[0]].size;
            if(sNodeg[x].son[1])
                sNodeg[x].size += sNodeg[sNodeg[x].son[1]].size;
        }
    }
    //解释:lazy数组存在的最上层的Node的结点本来就保证内容正确,但他下面的子树无法保证正确
    //作用:将x没有传递下去的操作传递给两个子树,使得子树的数据正确
    inline void PushDown(int x)
    {
        if(x && sNodeg[x].tag)
        {
            sNodeg[sNodeg[x].son[1]].tag ^= 1;
            sNodeg[sNodeg[x].son[0]].tag ^= 1;
            swap(sNodeg[x].son[1],sNodeg[x].son[0]);
            sNodeg[x].tag=0;
        }
    }
    //作用:x通过左旋或者右旋顶替父亲结点(并且如果父亲结点是根的话要更新root)
    inline void Rotate(int x)
    {
        int fa = sNodeg[x].fa;
        int ffa = sNodeg[fa].fa;
        PushDown(fa), PushDown(x);//因为fa在上面,所以先传递fa
        bool w = Which(x);//w==1是fa的右子树
        sNodeg[fa].son[w] = sNodeg[x].son[w^1];
        if(sNodeg[x].son[w^1])
            sNodeg[sNodeg[fa].son[w]].fa=fa;
        sNodeg[fa].fa=x;
        sNodeg[x].fa=ffa;
        sNodeg[x].son[w^1]=fa;
        if(ffa)
        {
            sNodeg[ffa].son[sNodeg[ffa].son[1]==fa]=x;
        }
        UpDate(fa);//维护fa
        if(fa == rootg)
            rootg = x;
    }
    //作用:将x上升到父亲结点是goal
    inline void Splay(int x,int goal)
    {
        int fa = sNodeg[x].fa;
        int ffa = sNodeg[fa].fa;
        for(PushDown(x); fa != goal; fa = sNodeg[x].fa,ffa = sNodeg[fa].fa) //我觉得这里的pushDown是没用的,情况一rotatex自己会更新,情况而rotate(y)不影响x,rotate(x)自己也会更新,不rotate的时候,没必要下放rotate(不过如果是求和lazy另当别论,毕竟后面还要update(x)的,对!就是为了后面的update)
        {
            //不要忘记每次更新一下fa和ffa
            if(ffa != goal)//如果需要转移两次及以上
                (Which(x)^Which(fa))?Rotate(x):Rotate(fa);//位异或(^),不同才为1,即之字形转x,一字型转fa
            Rotate(x);
        }
        UpDate(x);
        rootg = x;//这一句加是为了防止splay(root)这种情况....离谱....这是我自己加的
    }
    //作用:找到中序遍历第order个数
    inline int FindOder(int order)
    {
        int x = rootg;
        while(1)
        {
            PushDown(x);
            if(order <= sNodeg[sNodeg[x].son[0]].size)
                x = sNodeg[x].son[0];
            else
            {
                order -= sNodeg[sNodeg[x].son[0]].size + 1;
                if(!order)
                    return x;//当根节点为要求点时就在此处(还未进入右子树)退出
                x = sNodeg[x].son[1];
            }
        }
    }
    //作用:颠倒中序遍历的[x,y]区间的数
    inline void Reverse(int x,int y)
    {
        int l = x-1, r = y+1;
        l = FindOder(l), r = FindOder(r);
        //解释:由于l肯定小于r,所以目标区间肯定是root(l)的右子树(r)的左子树
        Splay(l, 0);
        Splay(r, l);
        int pos = sNodeg[rootg].son[1];
        pos = sNodeg[pos].son[0];
        sNodeg[pos].tag ^= 1;
    }
    //作用:中序遍历展示value
    inline void SplayDfs(int now)
    {
        PushDown(now);
        if(sNodeg[now].son[0])
            SplayDfs(sNodeg[now].son[0]);
        if(sNodeg[now].value!=-inf_ && sNodeg[now].value!=inf_)
            cout << sNodeg[now].value<<" cnt:"<<sNodeg[now].cnt << " fa:"<<sNodeg[now].fa<<" size:"<<sNodeg[now].size<<endl;
        if(sNodeg[now].son[1])
            SplayDfs(sNodeg[now].son[1]);
    }
    //使用指南:这个只是单纯的搜索函数,如果真的使用搜索应当调用search函数,因为根据splayTree,每搜索一次还要splay一下
    //作用:找到value所在的结点所对应的nodeNum
    //参数:x,当前根节点;value:目标值
    //返回值:有这么个结点,就返回这个结点的nodeNum,没有就返回NULL
    //附加返回值:parent(返回的是value对应结点的parent)
    //前提要求:tree的结构是按照value从小到达的
    int SearchValue(int x, valueType_ value, int &fa)
    {
        if(x == 0)
            return 0;
        if(value < sNodeg[x].value)//在左子树
        {
            fa = x;
            return SearchValue(sNodeg[x].son[0], value, fa);
        }
        else if(sNodeg[x].value < value)
        {
            fa = x;
            return SearchValue(sNodeg[x].son[1], value, fa);
        }
        return x;//有这么个数
    }
    //查找函数,带调整功能
    //参数:根结点,需要查找的val
    //返回:true or false
    bool Search(int x, int value)
    {
        int fa = 0;
        int temp = SearchValue(x, value, fa);
        if(temp && temp != rootg)
        {
            Splay(temp, 0);
            return true;
        }
        return false;
    }
    //作用:新填一个值为value下标为nodeNum的结点
    //返回值:新建结点的下标
    inline int CreatSplayNode(int value)
    {
        ++nodeNumg;
        sNodeg[nodeNumg].fa = 0;
        sNodeg[nodeNumg].value = value;
        sNodeg[nodeNumg].cnt = 1;
        sNodeg[nodeNumg].size = 1;
        sNodeg[nodeNumg].tag = 0;
        return nodeNumg;
    }
    //作用:插入一个值为value的点,如果该点存在,就cnt++(tree的结构依旧要按照value从小到大来排序)
    //前提要求:tree的结构是按照value从小到达的
    //返回值:返回x
    inline int SplayInsert(valueType_ value)
    {
        int fa = 0;//不可忽略赋值
        int x = SearchValue(rootg, value, fa);
        if(x == 0)//需要插入数据
        {
            x = CreatSplayNode(value);
            sNodeg[x].fa = fa;
            if(fa)
            {
                if(sNodeg[fa].value > value)//x的value更小就搞成左节点,否则就搞成右节点
                    sNodeg[fa].son[0]=x;
                else
                    sNodeg[fa].son[1]=x;
                UpDate(fa);
            }
        }
        else
            sNodeg[x].cnt++;
        Splay(x, 0);//x和他的祖先们都会在这个操作中得到update,所以不用担心
        return x;
    }
    //功能:中序遍历查找value的前驱(0)或者后继(1)
    //答案:如果答案没有前驱或者后驱,先插入再删除
    inline int NextHalf(int x,int op)
    {
        /**
        //fa根节点,此时x的父节点(存在的话)就是根节点
        if(fa && sNodeg[fa].value > value && op && sNodeg[x].son[1]==0)
            return fa;//如果当前节点的值大于x并且要查找的是后继
        if(fa && sNodeg[fa].value < value && !op && sNodeg[x].son[0]==0)
            return fa;//如果当前节点的值小于x并且要查找的是前驱
        //下面三行存疑?????
        **/
        x = sNodeg[x].son[op];//查找后继的话在右儿子上找,前驱在左儿子上找
        while(sNodeg[x].son[op^1])
            x = sNodeg[x].son[op^1];//要反着跳转,否则会越来越大(越来越小)
        return x;//返回位置
    }
    inline int Next(int value,int op)
    {
        int fa = 0;
        int x = SearchValue(rootg, value, fa);//从根节点开始访问,找出value==value的结点返回其nodeNum
        int ans = 0;
        if(x == 0)
        {
            x = SplayInsert(value);
            Splay(x, 0);
            ans = NextHalf(x, op);
            Delete(value);
            nodeNumg--;
        }
        else
        {
            Splay(x, 0);
            ans = NextHalf(x, op);
        }
        //cout <<value<<" "<<op<<"ans"<< ans << endl;
        return ans;
    }
    
    //作用:删除一个值为value的结点
    //返回:如果删除成功了true,如果树上根本就没有这个点false(但我觉得这应该不会发生,要不然next哪里找?)
    bool Delete(valueType_ value)
    {
        //下面四行就很妙啊!!要删除一个区间也是这么删除的
        int last = Next(value, 0);//查找value的前驱
        int next = Next(value, 1);//查找value的后继
        Splay(last,0);
        Splay(next,last);
        //
        int x = sNodeg[next].son[0];//后继的左儿子就是x
        if(sNodeg[x].cnt > 0)
        {
            sNodeg[x].cnt--;
            if(sNodeg[x].cnt)
                Splay(x, 0);
            else//这个节点直接丢掉(不存在了)
            {
                sNodeg[next].son[0] = 0;
                Splay(next, 0);//要update一下
            }
            return true;
        }
        else
            return false;
    }
    //作用:计算value排序为多少
    int CountOder(int value)
    {
        int fa = 0;
        int x = SearchValue(rootg, value, fa);//从根节点开始访问,找出value==value的结点返回其nodeNum
        int ans;
        if(x == 0)
        {
            x = SplayInsert(value);
            Splay(x, 0);
            ans = sNodeg[sNodeg[x].son[0]].size;//加上了自己的1,还得把-inf_减去
            Delete(value);
            nodeNumg--;
        }
        else
        {
            Splay(x, 0);
            ans = sNodeg[sNodeg[x].son[0]].size;
        }
        return ans;
    }
    //作用:找到第k大的数值为多少
    //返回值:对应点的value
    int FindKthNumber(int k)
    {
        int x = rootg;
        if(sNodeg[x].size < k)
            return 0;//上来就不满足条件
        while(1)
        {
            int ls = sNodeg[x].son[0];
            if(k > sNodeg[ls].size)
            {
                if(k > sNodeg[ls].size+sNodeg[x].size)
                {
                    k -= sNodeg[ls].size+sNodeg[x].size;
                    x = sNodeg[x].son[1];
                }
                else
                    return sNodeg[x].value;
            }
            else
                x = ls;
        }
    }
    这个版本的注释值得一看,思路也值得一看,但拘泥于函数可移植性,没写对

    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    typedef struct SplayNode *Tree;///typedef就是给A一个新名字B,此处是给struct SPLAY_NODE *新名字为S_NODE
    typedef int ElementType;
    struct SplayNode
    {
        Tree parent; //该结点的父节点,方便操作
        ElementType val; //结点值
        Tree lchild;
        Tree rchild;
        SplayNode(int val=0) //默认构造函数
        {
            parent=NULL;
            lchild=rchild=NULL;
            this->val=val;
        }
    };
    
    bool search(Tree &,ElementType);
    Tree *search_val(Tree&,ElementType,Tree&);
    bool insert(Tree &,ElementType);
    Tree left_single_rotate(Tree&,Tree);
    Tree right_single_rotate(Tree &,Tree );
    void LR_rotate(Tree&,Tree );
    void RL_rotate(Tree&,Tree );
    void right_double_rotate(Tree&,Tree );
    void left_double_rotate(Tree&,Tree );
    void SplayTree(Tree &,Tree);
    void up(Tree &,Tree );
    Tree *Find_Min(Tree &);
    void remove(Tree &,ElementType);
    
    //查找函数,带调整功能
    //参数:根结点,需要查找的val
    //返回:true or false
    bool search(Tree &root,ElementType val)
    {
    
        Tree parent=NULL;
        Tree *temp=NULL;
        temp=search_val(root,val, parent);
        if (*temp && *temp!=root)
        {
            SplayTree(root,*temp);
            return true;
        }
        return false;
    }
    
    //具体的查找函数
    //参数:根,需要查找的val,父节点指针
    //成功:返回其结点
    //失败:返回其引用,方便后面的插入操作
    Tree *search_val(Tree &root,ElementType val,Tree &parent)
    {
        if (root==NULL)
            return &root;
        if (root->val>val)
            return search_val(root->lchild,val,parent=root);
        else if(root->val<val)
            return search_val(root->rchild,val,parent=root);
        return &root;
    }
    
    //插入函数
    //参数:根,需要插入的val
    //返回:true or false
    bool insert(Tree &root,ElementType val)
    {
        Tree *temp=NULL;
        Tree parent=NULL;
        //先查找,如果成功则无需插入,否则返回该结点的引用。
        temp=search_val(root,val,parent);
    
        if (*temp==NULL) //需要插入数据
        {
            Tree node=new SplayNode(val);
            *temp=node; //因为是引用型,所以这里直接赋值,简化了很多了。
            node->parent=parent; //设置父节点。
            return true;
        }
        return false;
    }
    //单左旋操作
    //参数:根,旋转结点(旋转中心)
    //返回:当前子树中的新根
    Tree left_single_rotate(Tree &root,Tree node)
    {
        if (node==NULL)
            return NULL;
        Tree parent=node->parent; //其父结点
        Tree grandparent=parent->parent; //其祖父结点
        parent->rchild=node->lchild; //设置其父节点的右孩子
        if (node->lchild) //如果有左孩子则更新node结点左孩子的父节点信息
            node->lchild->parent=parent;
        node->lchild=parent; //更新node结点的左孩子信息
        parent->parent=node; //更新原父节点的信息
        node->parent=grandparent;
    
        if (grandparent) //更新祖父孩子结点的信息
        {
    
            if (grandparent->lchild==parent)
                grandparent->lchild=node;
            else
                grandparent->rchild=node;
        }
        else //不存在祖父节点,则原父节点为根,那么旋转后node为根
            root=node;
        return node;
    }
    //单右旋操作
    //参数:根,旋转结点(旋转中心)
    //返回:当前子树中的新根
    Tree right_single_rotate(Tree &root,Tree node)
    {
        if (node==NULL)
            return NULL;
        Tree parent,grandparent;
        parent=node->parent;
        grandparent=parent->parent;
        parent->lchild=node->rchild;
        if (node->rchild)
            node->rchild->parent=parent;
        node->rchild=parent;
        parent->parent=node;
        node->parent=grandparent;
        if (grandparent)
        {
            if (grandparent->lchild==parent)
                grandparent->lchild=node;
            else
                grandparent->rchild=node;
        }
        else
            root=node;
        return node;
    
    }
    //双旋操作(LR型),于AVL树类似
    //参数:根,最后将变成子树根结点的结点
    void LR_rotate(Tree &root,Tree node)
    {
        left_single_rotate(root,node); //先左
        right_single_rotate(root,node);//后右
    }
    //双旋操作(RL型),于AVL树类似
    //参数:根,最后将变成子树根结点的结点
    void RL_rotate(Tree&root,Tree node)
    {
        right_single_rotate(root,node); //先右后左
        left_single_rotate(root,node);
    }
    
    //两次单右旋操作
    //参数:根,最后将变成子树根结点的结点
    void right_double_rotate(Tree &root,Tree node)
    {
        right_single_rotate(root,node->parent); //先提升其父节点
        right_single_rotate(root,node);         //最后提升自己
    }
    //两次单左旋操作
    //参数:根,最后将变成子树根结点的结点
    void left_double_rotate(Tree &root,Tree node)
    {
        left_single_rotate(root,node->parent);
        left_single_rotate(root,node);
    }
    //Splay调整操作
    void SplayTree(Tree &root,Tree node)
    {
        while (root->lchild!=node && root->rchild!=node && root!=node) //当前结点不是根,或者不是其根的左右孩子,则根据情况进行旋转操作
            up(root, node);
        if (root->lchild==node) //当前结点为根的左孩子,只需进行一次单右旋
            root=right_single_rotate(root, node);
        else if(root->rchild==node) //当前结点为根的右孩子,只需进行一次单左旋
            root=left_single_rotate(root, node);
    }
    
    //根据情况,选择不同的旋转方式
    void up(Tree &root,Tree node)
    {
        Tree parent,grandparent;
        int i,j;
        parent=node->parent;
        grandparent=parent->parent;
        i=grandparent->lchild==parent ? -1:1;
        j=parent->lchild==node ?-1:1;
        if (i==-1 && j==-1) //AVL树中的LL型
            right_double_rotate(root, node);
        else if(i==-1 && j==1) //AVL树中的LR型
            LR_rotate(root, node);
        else if(i==1 && j==-1) //AVL树中的RL型
            RL_rotate(root, node);
        else                    //AVL树中的RR型
            left_double_rotate(root, node);
    }
    
    //操作当前子树的最小结点
    //返回:其最小结点的引用
    Tree *Find_Min(Tree &root)
    {
        if (root->lchild)
            return Find_Min(root->lchild);
        return &root;
    }
    
    //删除操作
    void remove(Tree &root,ElementType val)
    {
        Tree parent=NULL;
        Tree *temp;
        Tree *replace;
        Tree replace2;
        temp=search_val(root,val, parent); //先进行查找操作
        if(*temp) //如果查找到了
        {
            if (*temp!=root) //判断是否是根结点,不是根结点,则需要调整至根结点
                SplayTree(root, *temp);
    
            //调制根结点或者本来就是根结点后进行删除,先查看是否有替代元素
            if (root->rchild)
            {
                //有替代元素
                replace=Find_Min(root->rchild); //找到替换元素
                root->val=(*replace)->val;  //替换
                if ((*replace)->lchild==NULL) //左子树为空
                {
                    replace2=*replace;
                    *replace=(*replace)->rchild; //重接其右孩子
                    delete replace2;
    
                }
                else if((*replace)->rchild==NULL) //右子树为空
                {
                    replace2=*replace;
                    *replace=(*replace)->lchild; //重接其左孩子
                    delete replace2;
                }
            }
            else
            {
                //无替代元素,则根直接移向左子树,不管左子树是否为空都可以处理
                replace2=root;
                root=root->lchild;
                delete replace2;
            }
        }
    }
    
    //前序
    void PreOrder(Tree root)
    {
        if (root==NULL)
            return;
        printf("%d ",root->val);
        PreOrder(root->lchild);
        PreOrder(root->rchild);
    }
    //中序
    void InOrder(Tree root)
    {
        if (root==NULL)
            return;
        InOrder(root->lchild);
        printf("%d ",root->val);
        InOrder(root->rchild);
    }
    int main()
    {
        Tree root=NULL;
        insert(root, 11);
        insert(root, 7);
        insert(root, 18);
        insert(root, 3);
        insert(root, 9);
        insert(root, 16);
        insert(root, 26);
        insert(root, 14);
        insert(root, 15);
        search(root,14);
        printf("查找14:
    ");
        printf("前序:");
        PreOrder(root);
        printf("
    ");
        printf("中序:");
        InOrder(root);
        printf("
    ");
    
    //    remove(root,16);
    //    remove(root,26);
    //    remove(root,11);
        remove(root,16);
        printf("删除16:
    ");
        printf("前序:");
        PreOrder(root);
        printf("
    ");
        printf("中序:");
        InOrder(root);
        printf("
    ");
        return 0;
    }
    指针版本

    区间操作详解+部分代码(优)

    PART2(算法各种类型(并附上代码))

    PART3(算法的延伸应用)

    PART4(对算法深度的理解)

    PART5(与其相关的有趣题目)

     

  • 相关阅读:
    RedHat/CentOS根目录扩容
    VNC安装配置
    网络命名空间
    Linux 端口信息查看
    Linux实际常用命令
    yum的配置文件介绍
    Linux下查/删/替 命令(转)
    CentOS/redhat使用光盘镜像源
    数据库的附加和分离
    Corrupted Metadata/failed to mount /sysroot
  • 原文地址:https://www.cnblogs.com/bear-xin/p/15022674.html
Copyright © 2020-2023  润新知