zoukankan      html  css  js  c++  java
  • 浅谈平衡树之朝鲜树

    前置知识:

    BST二叉搜索树:

    度娘曰:

    若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。

    也就是说,你把它从根节点中序遍历一边就能得到一个从小到大的数列。

    大概长这样子:

     对于4:左边子树节点的权值为0 1 2 3,都比4小,右边子树节点的权值为5 6 7,都比4大。

    对于1:左边子树节点权值为0,比1小,右边子树节点权值为2 3,比1大(且比4小)。

    对于其他节点同理。

    一个不难理解的东西。

    code:以luoguP3369的模板为例子

    #include<cstdio>
    #include<algorithm>
    #define N 100010
    using namespace std;
    
    struct Node{
        int l,r;//左右节点
        int data;当前节点代表的值
        int siz,cnt;//以当前结点为根的子树的大小,以及当前结点代表的值出现的次数
    }t[N];
    
    int n,cnt,root;
    
    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;
    }
    
    void insert(int &rt,int x){//插入
        if(rt==0){//当前结点不存在,就创建新的节点并保存相关信息
            rt=++cnt;
            t[rt].data=x;
            t[rt].cnt=t[rt].siz=1;
            return;
        }
        if(t[rt].data==x){//正好查询到了
            t[rt].cnt++;
            t[rt].siz++;
            return;
        }
        if(t[rt].data>x){//要插的节点小于当前结点的值,根据BST性质,要往左子树找
            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){//第k大是什么数
        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;
    }

    朝鲜树:

    鬼才知道为什么叫做朝鲜树,但是和朝鲜没有半毛钱关系

    在BST中,我们或许会遇到以下情况:

     没错它和你一样偏科了。

    在上面的树中,毒瘤出题人如果让你查询最大的数,也就是右下角节点的树,你会发现它接近于O(n)了。而理想的情况是每一次查询复杂度为O(logn)。

    而无能的BST无法对其进行变通,于是大神们发明了平衡树算法,搞一搞BST变种,使得树的深度均摊一下,以保证复杂度均摊。

    朝鲜树就是平衡树的一种,

    原理:

    我们定义一个MAXdeep,表示整棵树最深的深度限制。如果当这棵树的深度超过了MAXdeep,就进行一次均摊重构,使得其在满足BST性质下,深度尽量保持最小。

    maxdeep根据数据可获取。

    建议跟着我的顺序,从main函数开始看

    code:

    #include<cstdio>
    #include<algorithm>
    #define N 100010
    using namespace std;

    struct Node{
        int l,r;
        int data;
        int siz,cnt;
        int dep,mxd;//不一样的地方。当前结点深度,最深深度限制
    }t[N];

    int n,cnt,root;//root表示当前整棵朝鲜树的根节点编号

    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;
        t[rt].mxd=max(t[l].mxd,t[r].mxd);//加入了maxdeep值的更改操作
    }

    bool cmp(const Node& a,const Node& b){//重构时需要根据大小排序,所以要用到sort
        return a.data<b.data;
    }

    int build(int l,int r,int dep){//圈3,重构函数,过程类似于线段树,很好理解。
        if(l>r) return 0;
        int mid=(l+r)>>1;
        t[mid].l=build(l,mid-1,dep+1);
        t[mid].r=build(mid+1,r,dep+1);
        t[mid].dep=dep;
        pushup(mid);
        return mid;
    }

    void rebuild(){//圈2,首先把所有节点进行从小到大排序,保证满足BST性质,然后建树,获取新树的根节点root。往上看到build:
        sort(t+1,t+1+cnt,cmp);
        root=build(1,cnt,1);
    }

    void insert(int &rt,int x,int dep){
        if(rt==0){
            rt=++cnt;
            t[rt].data=x;
            t[rt].cnt=t[rt].siz=1;
            t[rt].dep=t[rt].mxd=dep;//更新maxdeep
            return;
        }
        if(t[rt].data==x){
            t[rt].cnt++;
            t[rt].siz++;
            return;
        }
        if(t[rt].data>x){
            insert(t[rt].l,x,dep+1);
            pushup(rt);
            return;
        }
        if(t[rt].data<x){
            insert(t[rt].r,x,dep+1);
            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,1);
            }
            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);
            }
            if(t[root].mxd>100) rebuild();//圈1,如果深度大于100,我们就进行重构操作。向上跳到rebuild函数继续看。
        }
        return 0;
    }

    询问》排序》重构,这就是朝鲜树。

    完结。

     点击这里,继续学习替罪羊树。

  • 相关阅读:
    HDU 2121 Ice_cream’s world II 不定根最小树形图
    POJ 3164 Command Network 最小树形图
    POJ 3723 Conscription 最小生成树
    UVA 1175 Ladies' Choice 稳定婚姻问题
    BZOJ 2753 [SCOI2012] 滑雪和时间胶囊 最小生成树
    BZOJ 1854: [Scoi2010]游戏 无向图判环
    HDU 3974 Assign the task 暴力/线段树
    Codeforces Round #302 (Div. 2) D. Destroying Roads 最短路
    uoj 67 新年的毒瘤 割点
    蓝桥
  • 原文地址:https://www.cnblogs.com/lbssxz/p/12864754.html
Copyright © 2011-2022 走看看