zoukankan      html  css  js  c++  java
  • 替罪羊树

    一般的平衡树都是基于旋转来保证树的平衡,也就是让树不会太高,但替罪羊树是用暴力重构来保证
    具体的,就是插入时如果一个节点的左或右子树,节点数大于根的节点数乘一个平衡因子 alpha,那么就重构以这个节点为根的整个子树

    inline int isbad(tr *tree){
    	return tree->ls->cnt>alpha*tree->cnt+5||tree->rs->cnt>alpha*tree->cnt+5;
    }
    

    这个加五,是为了防止在子树较小的时候造成多次重构

    但是,如果某一次插入时根节点到插入节点的路径上,有好多节点都满足这个条件,那会造成多次重构
    这显然是十分浪费的,所以用一个 badtag 记录下最靠上的一个满足条件的点,然后只重构一次

    重构过程就是先来一遍 dfs(中序遍历),把所以节点存在一个指针数组里,然后指针数组里节点的值肯定是递增的,那么重构的递归里,就把当前下标范围 ((l,r))(mid) 作为节点,然后递归的重构 ((l,mid-1),(mid+1,r))

    至于删除,是惰性删除,给要删的节点打一个删除标记,然后重构的时候把它删掉
    所以保存的大小一个是确实存在的节点数(不包含已经删了的),和节点总数(包含已经删了的)。显然, isbad 比较的是节点总数,因为节点总数反映的才是子树的重量(或者说高度)

    剩余的就和普通的二叉搜索树差不多了,注意如果有多个相同的值,一个节点只代表一个
    可以说代码是非常好理解

    https://www.luogu.com.cn/problem/P6136
    https://www.luogu.com.cn/problem/P3369

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define alpha 0.7
    struct tr{
    	tr *ls,*rs;
    	int val,cnt,size;//size:num of not deleted,cnt:all
    	int deleted;
    }*null,*root,*nodes[100005],**badtag;
    int node_num;
    inline int isbad(tr *tree){
    	return tree->ls->cnt>alpha*tree->cnt+5||tree->rs->cnt>alpha*tree->cnt+5;
    }
    void dfs(tr *tree){
    	if(tree==null) return;
    	dfs(tree->ls);
    	if(!tree->deleted) nodes[++node_num]=tree;
    	dfs(tree->rs);
    	if(tree->deleted) delete tree;
    }
    tr *build(int l,int r){
    	if(l>r) return null;
    	if(l==r){
    		nodes[l]->ls=nodes[l]->rs=null;
    		nodes[l]->cnt=nodes[l]->size=1;
    		return nodes[l];
    	}
    	int mid=(l+r)>>1;
    	tr *tree=nodes[mid];
    	tree->ls=build(l,mid-1);tree->rs=build(mid+1,r);
    	tree->cnt=tree->size=1+tree->ls->size+tree->rs->size;
    	return tree;
    }
    inline void rebuild(tr *&tree){
    	node_num=0;
    	dfs(tree);
    //		std::printf("size : %d
    ",node_num-1);
    	tree=build(1,node_num);
    }
    void insert(tr *&tree,int x){
    	if(tree==null){
    		tree=new tr;
    		tree->ls=tree->rs=null;
    		tree->deleted=0;tree->val=x;
    		tree->size=tree->cnt=1;
    		return;
    	}
    	tree->size++;tree->cnt++;
    	if(x>tree->val) insert(tree->rs,x);
    	else insert(tree->ls,x);
    	if(isbad(tree)) badtag=&tree;
    }
    inline void insert_(tr *&tree,int x){
    	badtag=&null;
    	insert(tree,x);
    	if(badtag!=&null) rebuild(*badtag);
    }
    inline int rank(tr *tree,int x){
    	reg int ans=1;
    	while(tree!=null){
    		if(x<=tree->val) tree=tree->ls;
    		else{
    			ans+=tree->ls->size+!tree->deleted;
    			tree=tree->rs;
    		}
    	}
    	return ans;
    }
    inline int kth(tr *tree,int rk){
    	while(tree!=null){
    		if(!tree->deleted&&tree->ls->size+1==rk) return tree->val;
    		if(rk<=tree->ls->size) tree=tree->ls;
    		else{
    			rk-=tree->ls->size+!tree->deleted;
    			tree=tree->rs;
    		}
    	}
    }
    void erase(tr *tree,int rk){
    	if(!tree->deleted&&rk==tree->ls->size+1){
    		tree->deleted=1;
    		tree->size--;
    		return;
    	}
    	tree->size--;
    	if(rk<=tree->ls->size+!tree->deleted) erase(tree->ls,rk);
    	else erase(tree->rs,rk-tree->ls->size-!tree->deleted);
    }
    int main(){
    	int n=read();
    	null=new tr;
    	null->ls=null->rs=NULL;
    	null->deleted=null->cnt=null->size=null->val=0;
    	root=null;
    	reg int op,x;
    	while(n--){
    		op=read();x=read();
    		if(op==1) insert_(root,x);
    		else if(op==2) erase(root,rank(root,x));
    		else if(op==3) printf("%d
    ",rank(root,x));
    		else if(op==4) printf("%d
    ",kth(root,x));
    		else if(op==5) printf("%d
    ",kth(root,rank(root,x)-1));
    		else printf("%d
    ",kth(root,rank(root,x+1)));
    	}
    	return 0;
    }
    
  • 相关阅读:
    实现Android ListView 自动加载更多内容
    Android中通过GPS或NetWork获取当前位置的经纬度
    android检测当前网络是否可用
    怎么样获得泛型T的Class对象?
    slidemenu
    linux mysql 操作命令
    解决Skyline 6.5版本中3DML模型单体化后外部网页挂接问题
    解决Skyline6.5多球对比时,自动运行TerraExplorer软件的问题
    OSGeo.OGR.Geometry
    TerraExplorer Add-ons 和TEZ使用说明
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13393086.html
Copyright © 2011-2022 走看看