zoukankan      html  css  js  c++  java
  • splay(普通平衡树)

    平衡树是一种很玄学的操作,这里提供两种基本模板

    指针模板:

    这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    const int INF=1e9;
    const int MAX_Q = 1e5 + 10;
    int n,top=0;
    struct node{
        int v,size,cnt;  //cnt是避免这个位置上有多个相同权值的节点 
        node *ch[2],*pre;  //前驱和儿子 
        void update()
        {
            size=ch[0]->size+ch[1]->size+cnt;
        }
        int get_wh()
        {
            return pre->ch[0]==this ? 0:1;  //当前节点是爸爸的左儿子还是右儿子 
        }
        void set_ch(int wh,node *child);
    }pool[MAX_Q],*root,*null;
    
    void node::set_ch(int wh,node *child)  //建儿子 *取地址符,变参 
    {
        ch[wh]=child;
        if (child!=null) child->pre=this;
        update();
    }
    
    inline node *get_new(int v)
    {
        node *now=pool + ++top;  //获取新结点的指针;
        now->size=1;
        now->v=v;
        now->cnt=1;
        now->pre=now->ch[0]=now->ch[1]=null;
        return now; 
    }
    
    inline void rotate(node *&now)
    {
        node *old_father=now->pre;  //爸爸 
        node *grand=now->pre->pre;  //爷爷 
        int wh=now->get_wh();
        old_father->set_ch(wh,now->ch[wh^1]);  //我的'这个和爸爸方向一样的'儿子变成爸爸的儿子 
        now->set_ch(wh^1,old_father);  //爸爸变成我的这个儿子 
        now->pre=grand;
        if (grand!=null)
           grand->ch[grand->ch[0]==old_father ? 0:1]=now;  //我变成爷爷的儿子 
    }
    
    inline void splay(node *now,node *tar)
    {
        for (;now->pre!=tar;rotate(now))  //每两次中都要转一次当前节点,所以直接把rotate(now)写在了循环里 
           if (now->pre->pre!=tar)  //爸爸的爸爸不是目标,也就是至少还要旋转两次 
                now->pre->get_wh()==now->get_wh() ? 
                   rotate(now->pre):rotate(now);  //爸爸和儿子的方向相同,转爸爸,否则转儿子 
        if (tar==null) root=now;
    }
    
    void insert(int v) //插入 
    {
        node *last=null;  //从哪来的 
        node *now=root;
        node *newone=get_new(v);   //获取新节点 
        while (now!=null)
        {
            last=now;
            if (now->v==newone->v)
            {
                now->size++;
                now->cnt++;
                splay(now,null);
                return;
            }
            else
                if (newone->v<now->v)  //以权值为维护小根堆的标准 
                   now=now->ch[0];
                else
                  now=now->ch[1];
        }
        if (last==null)  //树为空 
           root=newone;
        else
        {
            if (newone->v<last->v)
               last->set_ch(0,newone);
            else
               last->set_ch(1,newone);
            splay(newone,null);
        }
        return; 
    }
    
    inline node *find(int v)
    {
        node *now=root;
        while (now!=null)
        {
            if (now->v==v)
                break;
            if (v<now->v)
                now=now->ch[0];
            else
                now=now->ch[1];
        }
        if (now!=null) splay(now,null);
        return now;
    }
    
    inline void del(int v)
    {
        node *now=find(v);
        if (now==null) return;
        if (now->cnt>1)
        {
            now->cnt--;
            now->size--;
            return;
        }
        if (now->ch[0]==null&&now->ch[1]==null)
           root=null;
        else if (now->ch[1]==null)
        {
            now->ch[0]->pre=null;
            root=now->ch[0];
        }
        else if (now->ch[0]==null)
        {
            now->ch[1]->pre=null;
            root=now->ch[1];
        }
        else
        {
            node *_=now->ch[0];
            while (_->ch[1]!=null) _=_->ch[1];
            splay(_,now);
            _->set_ch(1,now->ch[1]);
            _->pre=null;
            root=_;
        }
        return;
    }
    
    inline int pre(int v)
    {
        int ans=-INF;
        node *now=root;
        while (now!=null)
        {
            if (now->v<v)
               ans=max(ans,now->v),now=now->ch[1];
            else
               now=now->ch[0];
        }
        return ans;
    }
    
    inline int nxt(int v)
    {
        int ans=INF;
        node *now=root;
        while (now!=null)
        {
            if (now->v>v)
               ans=min(ans,now->v),now=now->ch[0];
            else
               now=now->ch[1];
        }
        return ans;
    }
    
    inline int get_rank(int v)
    {
        node *now=root;
        int left=0;
        while (now!=null)
        {
            if (now->v==v)
            {
                int ans=left+now->ch[0]->size+1;
                splay(now,null);
                return ans;
            }
            if (v<now->v)
               now=now->ch[0];
            else
               left+=now->ch[0]->size+now->cnt,now=now->ch[1];
        }
        return -1;
    }
    
    inline int kth(int k)
    {
        node *now=root;
        int left=0;
        while (now!=null)
        {
            int _=left+now->ch[0]->size;
            if (_+1<=k&&k<=_+now->cnt)
            {
                splay(now,null);
                return now->v;
            }
            if (k<=_)
               now=now->ch[0];
            else
               left=_+now->cnt,now=now->ch[1];  //先前计算的_就已经累加了left,所以这里一定是"=" 
        }
        return -1;
    }
    
    int main()
    {
        null=pool;
        null->v=0;
        null->size=0;
        null->cnt=0;
        null->pre=null->ch[0]=null->ch[1]=null;
        root=null;
    
        /*
        1. 插入x数
        2. 删除x数(若有多个相同的数,因只删除一个)
        3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
        4. 查询排名为x的数
        5. 求x的前驱(前驱定义为小于x,且最大的数)
        6. 求x的后继(后继定义为大于x,且最小的数)*/
        int q;
        scanf("%d",&q);
        while (q--)
        {
            int order,_;
            scanf("%d",&order);
            scanf("%d",&_);
            switch(order) 
            {
                case 1:
                    insert(_);
                    break;
                case 2:
                    del(_);
                    break;
                case 3:
                    printf("%d
    ",get_rank(_));
                    break;
                case 4:
                    printf("%d
    ",kth(_));
                    break;
                case 5:
                    printf("%d
    ",pre(_));
                    break;
                case 6:
                    printf("%d
    ",nxt(_));
                    break;
            }
        }
        return 0;
    }

    数组模板:

    这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    const int N=1000010;
    int ch[N][2],pre[N],size[N],v[N],cnt[N];
    int top=0,root=0;
    int n;
    
    void clear(int bh)
    {
        ch[bh][0]=ch[bh][1]=pre[bh]=size[bh]=v[bh]=cnt[bh]=0;
        return;
    }
    
    int get(int bh)
    {
        return (ch[pre[bh]][0]==bh ? 0:1); 
    }
    
    void update(int bh)
    {
        if (bh){
            size[bh]=cnt[bh];
            if (ch[bh][0]) size[bh]+=size[ch[bh][0]];
            if (ch[bh][1]) size[bh]+=size[ch[bh][1]];
        }
        return;
    }
    
    void rotate(int bh)
    {
        int fa=pre[bh];
        int grand=pre[fa];
        int wh=get(bh);
        ch[fa][wh]=ch[bh][wh^1];
        pre[ch[fa][wh]]=fa;
        ch[bh][wh^1]=fa;
        pre[fa]=bh;
        pre[bh]=grand;
        if (grand)
           ch[grand][ch[grand][0]==fa ? 0:1]=bh;
        update(fa);
        update(bh);
        return;
    }
    
    void splay(int bh,int mb)
    {
        for (int fa;fa=pre[bh];rotate(bh))
            if (pre[fa]!=mb)
               rotate(get(bh)==get(fa) ? fa:bh);
        if (mb==0) root=bh;  ///
        return;
    }
    
    void insert(int x)
    {
        int now=root,fa=0;
        if (root==0)
        {
            top++;
            size[top]=1;
            cnt[top]=1;
            v[top]=x;
            ch[top][0]=ch[top][1]=pre[top]=0;
            root=top;  /////
            return;
        }
        while (1)
        {
            if (v[now]==x)
            {
                cnt[now]++;
                update(now);
                update(fa);  ///
                splay(now,0);
                return;
            }
            fa=now;
            now=ch[now][v[now]<x];
            if (now==0)
            {
                top++;
                size[top]=1;
                cnt[top]=1;
                v[top]=x;
                ch[top][0]=ch[top][1]=0;
                ch[fa][v[fa]<x]=top;
                pre[top]=fa;
                update(fa);
                splay(top,0);
                return;
            }
        }
    }
    
    int find(int x) //查找排名 
    {
        int now=root,ans=0;
        while (1)
        {
            if (v[now]>x) now=ch[now][0];
            else{
                ans+=(ch[now][0] ? size[ch[now][0]]:0);
                if (v[now]==x){
                    splay(now,0);
                    return ans+1;
                }
                ans+=cnt[now];
                now=ch[now][1];
            }
        }
    }
    
    int findx(int x)  //查找值 
    {
        int now=root;
        while (1)
        {
            if (ch[now][0]&&size[ch[now][0]]>=x) now=ch[now][0];
            else{
                int temp=(ch[now][0] ? size[ch[now][0]]:0);
                temp+=cnt[now];
                if (x<=temp) return v[now];
                x-=temp;
                now=ch[now][1];
            }
        }
    }
    
    int qian(int x)
    {
        int now=root,ans=0;
        while (now)
        {
            if (v[now]<x) {
                ans=max(ans,v[now]);
                now=ch[now][1];
            }
            else now=ch[now][0];
        }
        return ans;
    }
    
    int hou(int x)
    {
        int now=root,ans=10000010;
        while (now)
        {
            if (v[now]>x) {
                ans=min(ans,v[now]);
                now=ch[now][0];
            }
            else now=ch[now][1];
        }
        return ans;
    }
    
    int fro()
    {
        int now=root;
        if (ch[root][0]) now=ch[root][0];
        while (ch[now][1]) now=ch[now][1];
        return now;
    }
    
    void del(int x)
    {
        int now=root;
        find(x);
        if (cnt[root]>1) {  ///s
            cnt[root]--;
            return;  
        }
        if (!ch[root][0]&&!ch[root][1])
        {
            clear(root);
            root=0;
            return;
        }
        if (!ch[root][0])
        {
            int k=root;
            root=ch[root][1];
            pre[root]=0;  ///
            clear(k);
            return;
        }
        if (!ch[root][1])
        {
            int k=root;
            root=ch[root][0];
            pre[root]=0;  ///
            clear(k);
            return;
        }
        int wh=fro();
        int k=root;
        splay(wh,0);
        ch[root][1]=ch[k][1];
        pre[ch[k][1]]=root;
        clear(k);
        update(root);
        return;
    }
    
    int main()
    {
        /*
        1. 插入x数
        2. 删除x数(若有多个相同的数,因只删除一个)
        3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
        4. 查询排名为x的数
        5. 求x的前驱(前驱定义为小于x,且最大的数)
        6. 求x的后继(后继定义为大于x,且最小的数)*/
        int q;
        scanf("%d",&q);
        while (q--)
        {
            int order,_;
            scanf("%d",&order);
            scanf("%d",&_);
            switch(order) 
            {
                case 1:
                    insert(_);
                    break;
                case 2:
                    del(_);
                    break;
                case 3:
                    printf("%d
    ",find(_));
                    break;
                case 4:
                    printf("%d
    ",findx(_));
                    break;
                case 5:
                    printf("%d
    ",qian(_));
                    break;
                case 6:
                    printf("%d
    ",hou(_));
                    break;
            }
        }
        return 0;
    }
  • 相关阅读:
    Introduction to Mathematical Thinking
    学习 Unix 常用命令
    学习 《UNIX网络编程》
    学习编译并运行C代码
    Introduction to Mathematical Thinking
    Introduction to Mathematical Thinking
    CentOS 6和CentOS 7防火墙的关闭
    centOS 7下无法启动网络(service network start)错误解决办法(应该是最全的了。。。)
    虚拟机中的CentOS 7设置固定IP连接最理想的配置
    使用VMware安装CentOS7详请
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673612.html
Copyright © 2011-2022 走看看