模板【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(与其相关的有趣题目)

 

原文地址:https://www.cnblogs.com/bear-xin/p/15022674.html