子树大小平衡树

#include<cstdio>
#define MXN 100000+3
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define nil 0
#define LEFT false
#define RIGHT true
int val[MXN],size[MXN],fa[MXN],left[MXN],right[MXN],recycle[1001],root;
int ntop,rtop=-1,rcount;
int newnode(){
    int nw;
    if(rtop!=-1) nw=recycle[rtop--];
    else nw=++ntop;
    val[nw]=0;
    size[nw]=1;
    fa[nw]=nil;
    left[nw]=nil;
    right[nw]=nil;
    return nw;
}
int newnode(int k){
    int nw=newnode();
    val[nw]=k;
    return nw;
}
void rotate(int now){rcount++;
    if(fa[now]==nil) return;
    int f=fa[now],gf=fa[f],l=left[now],r=right[now];
    fa[now]=gf;
    if(gf!=nil){
        if(left[gf]==f) left[gf]=now;
        if(right[gf]==f) right[gf]=now;
    }
    if(left[f]==now){
        right[now]=f;
        left[f]=r;
        if(r!=nil) fa[r]=f;
    }
    if(right[f]==now){
        left[now]=f;
        right[f]=l;
        if(l!=nil) fa[l]=f;
    }
    fa[f]=now;
    size[f]=size[left[f]]+size[right[f]]+1;
    size[now]=size[left[now]]+size[right[now]]+1;
    if(fa[now]==nil) root=now;
    return;
}
void maintain(int now,bool type){
    int l=left[now],r=right[now],b=nil,rt;
    if(type==LEFT){
        if(size[left[l]]>size[r]){
            rotate(l);
            rt=l;
        }
        else if(size[right[l]]>size[r]){
            b=right[l];
            rotate(b);
            rotate(b);
            rt=b;
        }
        else return;
    }
    if(type==RIGHT){
        if(size[right[r]]>size[l]){
            rotate(r);
            rt=r;
        }
        else if(size[left[r]]>size[l]){
            b=left[r];
            rotate(b);
            rotate(b);
            rt=b;
        }
        else return;
    }
    maintain(left[rt],LEFT);
    maintain(right[rt],RIGHT);
    maintain(rt,LEFT);
    maintain(rt,RIGHT);
    return;
}
int search(int now,int k){
    if(now==nil) return nil;
    if(val[now]==k) return now;
    if(val[now]>k) return search(left[now],k);
    if(val[now]<k) return search(right[now],k);
}
int insert(int &now,int ins){
    if(root==nil) root=ins;
    else if(now==nil) now=ins;
    else{
        ++size[now];
        if(val[ins]<=val[now]){
            if(left[now]==nil){
                left[now]=ins;
                fa[ins]=now;
            }
            else insert(left[now],ins);
        }
        else{
            if(right[now]==nil){
                right[now]=ins;
                fa[ins]=now;
            }
            else insert(right[now],ins);
        }
        maintain(now,val[ins]>val[now]);
    }
    return ins;
}
void remove(int now,int k){
    if(now==nil) return;
    if(val[now]==k){
        if(left[now]==nil&&right[now]==nil){
            if(left[fa[now]]==now) left[fa[now]]=nil;
            else right[fa[now]]=nil;
            fa[now]=nil;
            recycle[++rtop]=now;
            return;
        }
        else if(left[now]==nil){
            if(left[fa[now]]==now) left[fa[now]]=right[now];
            else right[fa[now]]=right[now];
            fa[right[now]]=fa[now];
            fa[now]=nil;
            recycle[++rtop]=now;
            return;
        }
        else if(right[now]==nil){
            if(left[fa[now]]==now) left[fa[now]]=left[now];
            else right[fa[now]]=left[now];
            fa[left[now]]=fa[now];
            fa[now]=nil;
            recycle[++rtop]=now;
            return;
        }
        else{
            int t=left[now];
            while(right[t]!=nil) t=right[t];
            val[now]=val[t];
            size[now]--;
            remove(left[now],val[t]);
            return;
        }
    }
    else{
        size[now]--;
        if(val[now]>k) remove(left[now],k);
        else remove(right[now],k);
    }
    return;
}
int select(int now,int k){
    if(now==nil) return nil;
    if(size[left[now]]+1==k) return now;
    if(size[left[now]]+1>=k) return select(left[now],k);
    if(size[left[now]]+1<k) return select(right[now],k-size[left[now]]-1);
}
int rank(int now,int k){
    if(now==nil) return 1;
    if(val[now]>=k) return rank(left[now],k);
    if(val[now]<k) return rank(right[now],k)+size[left[now]]+1;
}
int get_prev(int now,int k){
    if(now==nil) return -0x6fffffff;
    if(val[now]<k) return max(val[now],get_prev(right[now],k));
    else return get_prev(left[now],k);
}
int get_succ(int now,int k){
    if(now==nil) return 0x6fffffff;
    if(val[now]>k) return min(val[now],get_succ(left[now],k));
    else return get_succ(right[now],k);
}
int main(){
    int n;
    scanf("%d",&n);
    root=nil;
    int p,x;
    while(n--){
        scanf("%d%d",&p,&x);
        if(p==1) insert(root,newnode(x));
        if(p==2) remove(root,x);
        if(p==3) printf("%d\n",rank(root,x));
        if(p==4) printf("%d\n",val[select(root,x)]);
        if(p==5) printf("%d\n",get_prev(root,x));
        if(p==6) printf("%d\n",get_succ(root,x));
    }
    return 0;
}

Size Balanced Tree

子树大小平衡树是一种平衡树,利用子树大小维护平衡。满足下面性质的二叉搜索树称为SBT:

除根外,每棵树的大小不小于其兄弟的儿子的大小。

性质十分简单。由于具有对称性(即若L和R互为兄弟,则L大小不小于R的儿子大小,同时R大小不小于L的儿子大小),因此可以使树保持平衡。

SBT基本操作为旋转。旋转与splay定义不同(貌似与很多平衡树相同),左旋一棵树指使其右子树的根成为这棵树新的根,右旋与左旋对称。

主要的维护利用maintain,分以下四种情况:

1、若某点右子树大小小于其左儿子的左子树大小,则右旋其本身,对称的情况是某点左子树大小小于其右儿子的右子树大小,则左旋其本身;

2、若某点右子树大小小于其左儿子的右子树大小,则先左旋其左子树,再右旋其本身,另一种情况与这种情况对称。

旋转之后,有一些树的大小发生变化,那么需要继续maintain,直到满足SBT性质。若上述maintain了子树A,则先maintainA的左右子树,再maintainA本身。

有一种对maintain的改良方法,即maintain时只关注某一个子树是否小于其兄弟的儿子。不过我并不感觉改良了多少,大概改良了maintain次数,不过只是把四种情况分开讨论,原本四种情况也不会重叠。

复杂度还是很优的,时间相当的情况下旋转次数应比splay少(只会这两种平衡树...)。

时间: 2024-08-14 11:46:14

子树大小平衡树的相关文章

左神算法进阶班5_1求二叉树中最大搜索子树大小

[题目] 给定一棵二叉树的头节点head,请返回最大搜索二叉子树的大小 [题解] 简化问题,想到该问题的解答应该有几种情形 第一种可能: 最大搜索二叉子树在head的左子树 第二种可能: 最大搜索二叉子树在head的右子树 第三种可能: 最大搜索二叉子树为自己:利用左子树的最大值与右子树的最小值 递归左子树,再递归右子树 信息1:左子树中最大搜索二叉树的大小 信息2:右子树中最大搜索二叉树的大小 信息3:左子树最大搜索二叉树的头结点 信息4:右子树最大搜索二叉树的头结点 信息5:左子树上的最大值

手写一个节点大小平衡树(SBT)模板,留着用

看了一下午,感觉有了些了解,应该没有错,有错希望斧正,感谢 #include<stdio.h> #include<string.h> struct s { int key,left,right,size; }tree[10010]; int top; void left_rot(int &x)// 左旋 { int y=tree[x].right; tree[x].right=tree[y].left; tree[y].left=x; tree[y].size=tree[x

【Treap模板详细注释】BZOJ3224-普通平衡树

模板题:D错因见注释 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int INF=0x7fffffff; 8 struct treap 9 { 10 treap* lson; 11 treap* rson; 12 int key;/

重学数据结构系列之——平衡树之SB Tree(Size Blanced Tree)

平衡树 1.定义 对于每个结点,左右两个子树的高度差的绝对值不超过1,或者叫深度差不超过1 为什么会出现这样一种树呢? 假如我们按照1-n的顺序插入到二叉排序树中,那么二叉排序树就退化成了一个有序链表,效率大大降低. 2.有关概念 所有平衡树基本由以下三个特征组成: 1.自平衡条件 2.旋转操作 3.旋转的触发 平衡树通过设置合理的自平衡条件,使得二叉排序树的查找.插入等操作的性能不至于退化到 O(n)O(n),并且在进行二叉排序树的查找.插入等操作时进行判断,如果满足其中某个旋转的触发条件,则

关于平衡树(Treap)

平衡树是什么? 其实平衡树就是支持旋转的二叉查找树. 什么是二叉查找树呢? 其实就是满足(左子树(全部节点) < 根 < 右子树(全部节点))的一棵树,比如↓ (注意并不是每个节点都要是满的,也就是说它不一定是一棵完全二叉树) 那么二叉查找树有什么用呢? 假如我们要查询第k大的数,我们从根节点开始往下走,假如左子树大小大于等于k,我们就进入左子树去找:如果左子树大小等于(k-1),就输出当前节点:假如左子树大小小于(k-1),我们就进入右子树找排名为(k-(左子树大小+1))的数. 这样由于树

平衡树解析

转自:yyb巨佬的平衡树 不知道splay是啥,,你也要知道平衡树是啥... 平衡树是一个神奇的数据结构, 对于任意一个节点,左儿子的值比它小,右儿子的值比它大 并且任意一棵子树单独拎出来也是一棵平衡树 就像这样.... 各位大佬请原谅我丑陋无比的图 上面这个丑陋的东西就是一棵平衡树,他现在很平衡,是一棵满二叉树,高度正好是logn... 但是.. 如果这个丑陋的东西极端一点,他就会变成这样... 这张图依然很丑 现在看起来,这个东西一点都不平衡... 二叉树退化成了一条链 如果要查询的话,,,

luoguP3369[模板]普通平衡树(Treap/SBT) 题解

链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT) #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #incl

普通平衡树——非旋转treap

题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大的数)6. 求x的后继(后继定义为大于x,且最小的数) n<=100000 所有数字均在-107到107内. 输入样例: 10 1 106465 4 1 1 317721 1 460929 1 644985 1 841

平衡树模板

https://www.luogu.org/problemnew/show/P3369 1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 #define MAXN 100010 5 int n,sons[MAXN][2],f[MAXN],size[MAXN],cnt[MAXN],value[MAXN],root,Size; 6 inline int read(){ //快读 7 int x=0,ff=1