• 平衡树 替罪羊树


    //平衡树 替罪羊树
    //不进行旋转 当左右子树重量相差过大(通常以3:1为界)就把树拍扁重构 
    //重构即以最中心节点为根节点 二分建树 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define lim 0.75//拍扁重构的标准 
    using namespace std;
    int n,root,size,cnt;
    int goat,flat[1000001];//需要拍扁重构的节点,拍扁重构时暂存的数组 
    struct uio{
        int son[2],val,siz,ava;//左右儿子,权值,子树大小+自己大小,未被删除的子树大小 
        bool exist;//标记是否被删除 
    }scg[1000001];
    bool check(int now)//判断是否拍扁 
    {
        if((double)scg[now].ava*lim<=(double)max(scg[scg[now].son[0]].ava,scg[scg[now].son[1]].ava))
        //这是说 如果此节点未被删除的节点总数的3/4比任意一棵子树未被删除的节点数少(即:一棵子树未被删除的节点数是另一棵的3倍还多) 
            return true;//需要拍扁 
        return false;//不用拍扁 
    }
    void dfs(int now)//中序遍历,找出需要参与重构的节点 
    {
        if(!now)
            return;
        dfs(scg[now].son[0]);
        if(scg[now].exist)
            flat[++cnt]=now;
        dfs(scg[now].son[1]);
    }
    void build(int l,int r,int &now)//构造过程 
    {
        int mid=(l+r)/2;
        now=flat[mid];
        if(l==r)
        {
            scg[now].son[0]=scg[now].son[1]=0;
            scg[now].siz=scg[now].ava=1;
            return;
        }
        if(l<mid)
            build(l,mid-1,scg[now].son[0]);
        else scg[now].son[0]=0;
        build(mid+1,r,scg[now].son[1]);
        scg[now].siz=scg[scg[now].son[0]].siz+scg[scg[now].son[1]].siz+1;
        scg[now].ava=scg[scg[now].son[0]].ava+scg[scg[now].son[1]].ava+1;
    }
    void rebuild(int &now)//重构 
    {
        cnt=0;
        dfs(now);
        if(cnt)
            build(1,cnt,now);
        else now=0;
    }
    int get_no(int k)
    {
        int now=root,ans=1;
        while(now)
        {
            if(scg[now].val>=k)
                now=scg[now].son[0];
            else
            {
                ans+=scg[scg[now].son[0]].ava+scg[now].exist;
                now=scg[now].son[1];
            }
        }
        return ans;
    } 
    void insert(int &now,int k)
    {
        if(!now)
        {
            now=++size;
            scg[now].val=k;
            scg[now].siz=scg[now].ava=1;
            scg[now].exist=true;
            scg[now].son[0]=scg[now].son[1]=0;
            return;
        }
        scg[now].siz++;
        scg[now].ava++;
        if(scg[now].val>=k)
            insert(scg[now].son[0],k);
        else insert(scg[now].son[1],k);
        if(!check(now))//此节点不需要重构 
        {//注:此处大括号不可去除!!!否则100行else会对应92行if而不是90行if导致多次拍扁重构! 
            if(goat)//子节点中有满足重构条件的点 
            {
                if(scg[now].son[0]==goat)
                    rebuild(scg[now].son[0]);
                else rebuild(scg[now].son[1]);
                goat=0;
            }
        }//注:此处大括号不可去除!!!否则100行else会对应92行if而不是90行if导致多次拍扁重构! 
        else goat=now;//此节点需要重构,则继续记录 
    }
    void del_pos(int &now,int k)
    {
        if(scg[now].exist&&scg[scg[now].son[0]].ava+1==k)
        {
            scg[now].exist=false;
            scg[now].ava--;
            return;
        }
        scg[now].ava--;
        if(scg[scg[now].son[0]].ava+scg[now].exist>=k)
            del_pos(scg[now].son[0],k);
        else del_pos(scg[now].son[1],k-scg[scg[now].son[0]].ava-scg[now].exist);
    }
    void del_val(int k)
    {
        del_pos(root,get_no(k));
        if((double)scg[root].siz*lim>=scg[root].ava)//若根节点被删除的节点过多(即:超过总结点的1/4) 
            rebuild(root);//重构根节点 
    }
    int get_num(int k)
    {
        int now=root;
        while(now)
        {
            if(scg[now].exist&&scg[scg[now].son[0]].ava+1==k)
                return scg[now].val;
            else if(scg[scg[now].son[0]].ava>=k)
                now=scg[now].son[0];
            else
            {
                k-=scg[scg[now].son[0]].ava+scg[now].exist;
                now=scg[now].son[1];
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            if(u==1)
                insert(root,v);
            if(u==2)
                del_val(v);
            if(u==3)
                printf("%d
    ",get_no(v));
            if(u==4)
                printf("%d
    ",get_num(v));
            if(u==5)
                printf("%d
    ",get_num(get_no(v)-1));
                //前驱:即k的位置的前一位 
            if(u==6)
                printf("%d
    ",get_num(get_no(v+1)));
                //后继:即比k大的第一个数的位置 不能直接加一是因为k可能不止一个 
        }
         return 0;
    }
  • 相关阅读:
    ios开发之--使用AFN上传3.1.0上传视频,不走成功回调原因及解决方法
    ios开发之--[_NSInlineData objectForKeyedSubscript:]
    swift开发之--代理协议的使用
    ios开发之--NSString的操作
    ios开发之--条用第三方地图路线导航
    ios开发之--多个按钮单选效果
    ios开发之--首页 导航栏隐藏 下一级页面显示,pop回来显示白条
    ios开发之--令UITableView滚动到指定位置
    Selenium 异常处理
    Selenium 选项卡管理
  • 原文地址:https://www.cnblogs.com/water-radish/p/9280885.html
Copyright © 2020-2023  润新知