[复习]平衡树splay

Posted xingmi-weiyouni

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[复习]平衡树splay相关的知识,希望对你有一定的参考价值。

#include<iostream>
#include<cstdio>
#include<cstring>
#define read(a) a=init()
using namespace std;
struct node
    long long fa,ch[2],data,size,cnt;
t[10000003];
long long n,root=0,tot=0,lei,thi;
inline long long init()//快读

    long long a=0,b=1;char ch=getchar();
    while(ch>9||ch<0)if(ch==-)b=-1;ch=getchar();
    while(ch>=0&&ch<=9)a=(a<<3)+(a<<1)+(ch-0);ch=getchar();
    return a*b;

inline void rotate(long long x)//基本旋转操作:旋转x

    long long y=t[x].fa,z=t[y].fa,k=t[y].ch[1]==x;//y是x的父节点,z是x的祖先节点,
                                                //k存储x是y的哪一个儿子
    t[z].ch[t[z].ch[1]==y]=x;
    t[x].fa=z;//祖为父:我变成我爷爷的儿子,我管我爷爷喊爸爸
    t[y].ch[k]=t[x].ch[k^1];
    t[t[x].ch[k^1]].fa=y;//子为孙:我儿子过继给我爸爸,我儿子管我爸爸喊爹
    t[x].ch[k^1]=y;
    t[y].fa=x;//父为子:我爸爸变成我儿子,我爸爸管我喊爹
    t[y].size=t[t[y].ch[0]].size+t[t[y].ch[1]].size+t[y].cnt;//更新节点规模
                                        //size存储以该节点为根节点的子树的大小(包括自身)
    t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+t[x].cnt;//由于此时y是x的子节点,所以先y后x

inline void splay(long long x,long long goal)//splay操作:将x旋转为goal的子节点

    while(t[x].fa!=goal)//若x的父节点不是目标节点
    
        long long y=t[x].fa,z=t[y].fa;//取出父节点和祖先节点
        if(z!=goal)//若祖先节点依旧不是目标节点,那么意味着我们至少需要旋转两次
            (t[z].ch[0]==y)^(t[y].ch[0]==x)?rotate(x):rotate(y);
                    //为了打乱原本平衡树上存在的一条链从而防止被卡,若x、y、z在同一条线上,先转y
        rotate(x);//无论之前转了y还是x或者是没有转,我们都需要再转一次x
    
    if(!goal)root=x;//更新根节点

inline void insert(long long x)//插入操作:插入一个数x,使平衡树仍然保持平衡

    long long u=root,fa=0;//从根节点开始搜,记得存储fa以更新x的节点信息中的fa
    while(u&&t[u].data!=x)//u仍然存在且当前节点u不等于目标节点,迭代向下查找。
    
        fa=u;
        u=t[u].ch[x>t[u].data];//x小于当前节点信息,向左查找,否则向右查找。(利用平衡树本身性质)
    
    if(u)t[u].cnt++;//如果本身存在该节点,该节点个数加一
    else //否则新建一个节点(各个信息都要新建)
    
        u=++tot;
        if(fa)t[fa].ch[x>t[fa].data]=u;
        t[u].ch[0]=t[u].ch[1]=0;
        t[tot].fa=fa;
        t[tot].data=x;
        t[tot].cnt=1;
        t[tot].size=1;
    
    splay(u,0);

inline void find(long long x)//查找操作:找到元素x并将它旋转为根节点

    long long u=root;//u是当前节点,从根节点开始搜索。
    if(!u)return ;//如果没有根节点,即没有树:查询无效。
    while(t[u].ch[x>t[u].data]&&x!=t[u].data)//继续迭代向下查找x
        u=t[u].ch[x>t[u].data];
    splay(u,0);//转上去

inline long long nxt(long long x,long long jud)//查询操作:查询前驱或后继
                                    //(jud为0时表示查询前驱,jud为1时表示查询后继)
    find(x);//找到x并把他(或者和它最接近的那个点)转到根节点
    long long u=root;
    if(t[u].data>x&&jud)return u;
    if(t[u].data<x&&!jud)return u;//这两句话针对查询的x本身不在平衡树中
    u=t[u].ch[jud];//若该节点已经被旋转为根节点,前驱是它左子树最右边的儿子,后驱是它右子树最左边的儿子。
    while(t[u].ch[jud^1])u=t[u].ch[jud^1];
    return u;

inline void delet(long long x)//删除操作:删掉某一个节点

    long long pre=nxt(x,0);
    long long nt=nxt(x,1);//分别查询前驱和后继并存储
    splay(pre,0),splay(nt,pre);//把前驱转成根节点,把后继转成根节点的子节点。
        //分析可得此时nt是pre的右儿子,x是nt的左儿子且x没有子树
    long long res=t[nt].ch[0];
    if(t[res].cnt>1)
    
        t[res].cnt--;//若该节点不止一个,删掉一个
        splay(res,0);//并把该节点旋转至根节点
    
    else t[nt].ch[0]=0;//否则直接把它扔掉(nt的左儿子指向空)

inline long long k_th(long long x)//第k大操作:查询已插入的节点中第k大的数

    long long u=root;//临时节点u
    if(t[u].size<x)return 0;//若该点的规模小于x,即不可能存在第x大的数,返回0
    while(1)
    
        int y=t[u].ch[0];//取出左儿子方便查询规模
        if(x>t[y].size+t[u].cnt)//如果所查询的数大于左儿子的规模
        
            x-=t[y].size+t[u].cnt;//往右找,查询的第k大数即是右儿子中x-t[y].size+t[u].cnt
            u=t[u].ch[1];
        
        else 
            if(t[y].size>=x) u=y;//如果儿子查询
            else return t[u].data;//
    

int main()

    read(n);
    insert(0x7fffffff);
    insert(-0x7fffffff);
    while(n--)
    
        read(lei),read(thi);
        switch(lei)
        
            case 1:
                insert(thi);
                break;
            
            case 2:
                delet(thi);
                break;
            
            case 3:
                find(thi);
                printf("%lld\n",t[t[root].ch[0]].size);
                break;
            
            case 4:
                printf("%lld\n",k_th(thi+1));
                break;
            
            case 5:
                printf("%lld\n",t[nxt(thi,0)].data);
                break;
            
            case 6:
                printf("%lld\n",t[nxt(thi,1)].data);
                break;
            
        
    
    return 0;

 

以上是关于[复习]平衡树splay的主要内容,如果未能解决你的问题,请参考以下文章

Splay伸展树入门(单点操作,区间维护)

平衡树——splay 一

平衡树之Splay

平衡树splay

# 伸展树 Splay

bzoj 3224: Tyvj 1728 普通平衡树 && loj 104 普通平衡树 (splay树)