浅谈平衡树之替罪羊树

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

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

替罪羊树

核心思想:

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

不同之处:朝鲜树是记录整棵树的最大深度,如果超出规定值就进行全树重构。而替罪羊树是在插入(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普通平衡树

完结撒花~

原文地址:https://www.cnblogs.com/lbssxz/p/12912780.html