• 浅谈平衡树之替罪羊树


    “其实现容易但时间复杂度较不理想,可以被应用在较不赶时间的资讯解题竞赛 被认为是替罪羊树的劣质仿制品”——百度百科

    前置知识:劣质仿制品朝鲜树。

    替罪羊树

    核心思想:

    替罪羊树核心思想和朝鲜树相似:发现异常(不平衡)的情况就进行重构。

    不同之处:朝鲜树是记录整棵树的最大深度,如果超出规定值就进行全树重构。而替罪羊树是在插入(insert)时,询问以当前节点为根的子树是否不平衡,不平衡的话就重构子树。

    可以明显感觉到,重构子树和重构整棵树相比,优化了不少。所以说朝鲜树是劣质仿制品。不过朝鲜树好打。。。

    具体操作:

    在插入函数中,首先要按照惯例询问当前节点是否存在,如果不存在,进行什么操作都没意义,不存在的话就按照惯例进行建立节点,修改信息,返回。如果存在,那么这个节点以及它的子树就很可能不平衡。这时我们在到左右子树进行下一层插入操作前,先进行rebuild函数操作,询问整棵子树是否需要重构。

    code:

    void insert(int &rt,int x){
        if(rt==0){
            rt=++cnt;
            t[rt].data=x;
            t[rt].cnt=t[rt].siz=1;
            return;
        }
        rebuild(rt);//奇怪的地方出现了
        if(t[rt].data==x){
            t[rt].cnt++;
            t[rt].siz++;
            return;
        }
        if(t[rt].data>x){
            insert(t[rt].l,x);
            pushup(rt);
            return;
        }
        if(t[rt].data<x){
            insert(t[rt].r,x);
            pushup(rt);
            return;
        }
    }

    而rebuild函数中,我们明确要做的事情:1,判断是否需要重构,如果是:2,进行重构,调用build和dfs中序遍历函数进行重构。

    code:

    int build(int l,int r){//与朝鲜树差不多的建树
        if(l>r) return 0;
        int mid=(l+r)>>1;
        t[p[mid]].l=build(l,mid-1);
        t[p[mid]].r=build(mid+1,r);
        pushup(p[mid]);
        return p[mid];
    }
    
    void dfs(int rt){//套上了dfs壳子的中序遍历操作,目的是根据BST的性质从小到大获取子树所有节点信息,一个个排到vector里
        if(rt==0) return;
        dfs(t[rt].l);
        p.push_back(rt);
        dfs(t[rt].r);
    }
    
    inline void rebuild(int &rt){//rebuild本尊。
        if(rt==0) return;
        if(t[rt].siz*0.7<t[t[rt].l].siz||
        t[rt].siz*0.7<t[t[rt].r].siz){
    //0.7是偏移值。上述判断语句不难看出是在比较左右子树的节点个数。我们判断左右子树是否不平衡的依据就是看其中一个子树是否占据了整棵树的70%甚至还要多。而0.7这个数据是计算出来的最佳值,大一点会影响到遍历时的复杂度,小一点会过多增加重构次数,0.7是适中值。
            p.clear();//已经确定要重构了,清除上一次数据
            p.push_back(-1);//占个位子,让下标从1开始
            dfs(rt);//中序遍历获取子树信息
            rt=build(1,p.size()-1);//下标从1开始的好处
        }
    }

    整个代码:

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #define N 100010
    using namespace std;
    
    struct Node{
        int l,r;
        int data;
        int siz,cnt;
    }t[N];
    
    int n,cnt,root;
    vector<int> p;
    
    void pushup(int rt){
        int l=t[rt].l;
        int r=t[rt].r;
        t[rt].siz=t[l].siz+t[r].siz+t[rt].cnt;
    }
    
    int build(int l,int r){
        if(l>r) return 0;
        int mid=(l+r)>>1;
        t[p[mid]].l=build(l,mid-1);
        t[p[mid]].r=build(mid+1,r);
        pushup(p[mid]);
        return p[mid];
    }
    
    void dfs(int rt){
        if(rt==0) return;
        dfs(t[rt].l);
        p.push_back(rt);
        dfs(t[rt].r);
    }
    
    inline void rebuild(int &rt){
        if(rt==0) return;
        if(t[rt].siz*0.7<t[t[rt].l].siz||
        t[rt].siz*0.7<t[t[rt].r].siz){
            p.clear();
            p.push_back(-1);
            dfs(rt);
            rt=build(1,p.size()-1);
        }
    }
    
    void insert(int &rt,int x){
        if(rt==0){
            rt=++cnt;
            t[rt].data=x;
            t[rt].cnt=t[rt].siz=1;
            return;
        }
        rebuild(rt);
        if(t[rt].data==x){
            t[rt].cnt++;
            t[rt].siz++;
            return;
        }
        if(t[rt].data>x){
            insert(t[rt].l,x);
            pushup(rt);
            return;
        }
        if(t[rt].data<x){
            insert(t[rt].r,x);
            pushup(rt);
            return;
        }
    }
    
    int delmin(int &rt){
        if(t[rt].l){
            int ret=delmin(t[rt].l);
            pushup(rt);
            return ret;
        }
        int ret=rt;
        rt=t[rt].r;
        return ret;
    }
    
    void del(int &rt,int x){
        if(t[rt].data>x){
            del(t[rt].l,x);
            pushup(rt);
        }
        if(t[rt].data<x){
            del(t[rt].r,x);
            pushup(rt);
        }
        if(t[rt].data==x){
            if(t[rt].cnt>1){
                t[rt].cnt--;
                t[rt].siz--;
                return;
            }
            if(t[rt].l==0){
                rt=t[rt].r;
                return;
            }
            if(t[rt].r==0){
                rt=t[rt].l;
                return;
            }
            int tmp=delmin(t[rt].r);
            t[rt].data=t[tmp].data;
            t[rt].cnt=t[tmp].cnt;
            pushup(rt);
            return;
        }
    }
    
    int getk(int rt,int x){
        if(t[rt].data==x) return t[t[rt].l].siz+1;
        if(t[rt].data<x) return (t[rt].siz-t[t[rt].r].siz)+getk(t[rt].r,x);
        if(t[rt].data>x) return getk(t[rt].l,x);
    }
    
    int getkth(int rt,int x){
        if(t[t[rt].l].siz+1<=x&&x<=t[t[rt].l].siz+t[rt].cnt) return t[rt].data;
        if(t[t[rt].l].siz+1>x) return getkth(t[rt].l,x);
        if(x>t[t[rt].l].siz+t[rt].cnt) return getkth(t[rt].r,x-(t[t[rt].l].siz+t[rt].cnt));
    }
    
    int getpre(int rt,int x){
        int p=rt,ans;
        while(p){
            if(x<=t[p].data){
                p=t[p].l;
            }else{
                ans=p;
                p=t[p].r;
            }
        }
        return ans;
    }
    
    int getsuc(int rt,int x){
        int p=rt,ans;
        while(p){
            if(x>=t[p].data){
                p=t[p].r;
            }else{
                ans=p;
                p=t[p].l;
            }
        }
        return ans;
    }
    
    int main(){
        scanf("%d",&n);
        while(n--){
            int opt,x;
            scanf("%d%d",&opt,&x);
            if(opt==1){
                insert(root,x);
            }
            if(opt==2){
                del(root,x);
            }
            if(opt==3){
                printf("%d
    ",getk(root,x));
            }
            if(opt==4){
                printf("%d
    ",getkth(root,x));
            }
            if(opt==5){
                printf("%d
    ",t[getpre(root,x)].data);
            }
            if(opt==6){
                printf("%d
    ",t[getsuc(root,x)].data);
            }
        }
        return 0;
    }

    例题:洛谷p3369普通平衡树

    完结撒花~

  • 相关阅读:
    Oracle中的substr()函数和INSTR()函数和mysql中substring_index函数字符截取函数用法:计算BOM系数用量拼接字符串*计算值方法
    (转载)SDRAM驱动笔记
    【转】Verilog阻塞与非阻塞赋值使用要点
    【转转】(筆記) always block內省略else所代表的電路 (SOC) (Verilog)
    (原創) 如何處理signed integer的加法運算與overflow? (SOC) (Verilog)
    [转载]亚稳态
    Dev Exprss 发布部署
    Dev splliter 去除中间的分割显示
    DevTreeList中的新增、修改的设计
    Oracle 常用网址
  • 原文地址:https://www.cnblogs.com/lbssxz/p/12912780.html
Copyright © 2020-2023  润新知