替罪羊树笔记

前言
以前觉得树形结构都难的要死,虽然现在也没好多少,但是明显对树形结构的了解要远超从前了
替罪羊树是我偷听dalao对话时听来的数据结构,总觉得遥不可及,还十分霸气 其实似乎没有那么深奥,我想以替罪羊树作为跳板,撕开 平衡树 神秘的面纱
以上200%为瞎扯)这也是瞎扯)


特点
仔细想来,替罪羊树似乎与BST有几分相像,做为一棵平衡树,它会在子树失去平衡后 暴力重构
以保证子树满足平衡树的性质(定义一个平衡树因子 α α ,对替罪羊树的每个节点 t t ,都需要满足: m a x ( s i z e l , s i z e r ) α × s z i e t max(size_l,size_r) \leq α×szie_t ),而不是一般平衡树的旋转操作


时间复杂度
可以保证,除重构外,所有的操作的平摊最坏时间复杂度为 O ( l o g ( n ) ) O(log(n)) ,而重构是 O ( n ) O(n) ,但均摊下来也是 l o g log 级别,在可接受范围之内


各种操作

  • 插入

    插入的操作特别容易,
    就是暴力搜空缺处插入
//插入元素
inline void Insert(int &x,int val) {
	//若节点为空,将元素插入
	if (!x) {
		x=Void[tot--];
		node[x].Val=val;
		node[x].Exist=1;
		Build(x);
		
		return;
	}
	
	//子树大小+1
	++node[x].Size;
	++node[x].Fac;
	
	//将待插入元素与当前元素的大小比较,继续搜索当前节点的左子树或右子树
	if (val<=node[x].Val) {
		Insert(node[x].Son[0],val);
	}
	else {
		Insert(node[x].Son[1],val);
	}
}

  • 删除

    将节点标记为不存在,
    该节点所在的子树大小-1
//删除排名为rk的数
inline void Delete_rank(int &x,int rk) {
	//如果当前节点存在且排名为rk,删除节点
	if (node[x].Exist && !((node[node[x].Son[0]].Fac+1)^rk)) {
		//标记节点为不存在,节点所在子树大小-1
		node[x].Exist=0;
		--node[x].Fac;
		
		return;
	}

	//子树实际大小-1
	--node[x].Fac;
	
	//比较需删除元素与当前元素,确定搜索左子树还是右子树
	if (node[node[x].Son[0]].Fac+node[x].Exist>=rk) {
		Delete_rank(node[x].Son[0],rk);
	}
	else {
		Delete_rank(node[x].Son[1],rk-node[x].Exist-node[node[x].Son[0]].Fac);
	}
}

//删除值为v的数
inline void Delete_val(int v) {
	//将操作转化为删除排名为v的排名的数
	Delete_rank(rt,get_rank(v));

	//维护平衡树性质,0.5<=alpha<=1.0,一般取0.75
	if ((double)node[rt].Size*alpha>(double)node[rt].Fac) {
		ReBuild(rt);
	}
}

  • 查询

    询问值为val的数的排名 或 排名为rank的数的值
    暴力搜索
//询问值为val的数的排名
inline int get_rank(int v) {
	int x=rt,rk=1;
	//节点不为空时进行搜索
	while (x) {
		//判断当前元素是否大于v,是就访问左子数,否则累加当前元素排名,访问右子树
		if (node[x].Val>=v) {
			x=node[x].Son[0];
		}
		else {
			rk+=node[node[x].Son[0]].Fac+node[x].Exist;
			x=node[x].Son[1];
		}
	}
	
	return rk;
}

//询问排名为rank的数的值
inline int get_val(int rk) {
	int x=rt;
	//节点不为空时进行搜索
	while (x) {
		//若当前元素排名为rk则返回,否则判断当前元素的排名是否小于rk,决定访问左子数,或访问右子树,rk减去当前元素排名
		if (node[x].Exist && node[node[x].Son[0]].Fac+1==rk) {
			return node[x].Val;
		}
		if (node[node[x].Son[0]].Fac>=rk) {
			x=node[x].Son[0];
		}
		else {
			rk-=node[x].Exist+node[node[x].Son[0]].Fac;
			x=node[x].Son[1];
		}
	}
}
  • 重构

    重构的过程就是把数压扁,然后吊起来挂着

    随便举个例子(

显然不合法,那就把它压扁

然后吊起来挂着

操作就完成了

//压扁树
inline void Traversal(int x) {
	//节点为空就退出
	if (!x) {
		return;
	}

	//中序遍历树,如果节点存在,就加入数组,否则删点,存入存储空点的数组
	Traversal(node[x].Son[0]);
	if (node[x].Exist) {
		cur[++cnt]=x;
	}
	else {
		Void[++tot]=x;
	}
	Traversal(node[x].Son[1]);
}

//挂起压扁的树
inline void SetUp(int l,int r,int &x) {
	//将中点设置为新树的根节点
	int mid=(l+r)>>1;
	x=cur[mid];

	//若当前节点为叶节点则重置当前节点
	if (l==r) {
		Build(x);
		
		return;
	}

	//如果当前元素左边有数,就说明它有左子树,于是重构它的左子树,否则它的左子树为空树
	if (l<mid) {
		SetUp(l,mid-1,node[x].Son[0]);
	}
	else {
		node[x].Son[0]=0;
	}

	//重构它的右子树
	SetUp(mid+1,r,node[x].Son[1]);
	PushUp(x);
}

//重构全程
inline void ReBuild(int &x) {
	cnt=0;
	Traversal(x);
	if (cnt) {
		SetUp(1,cnt,x);
	}
	else {
		x=0;
	}
}

模板题luoguP3369【模板】普通平衡树

代码:

#include <bits/stdc++.h>
#define N 100005
#define alpha 0.75
using namespace std;

int n,st,rt,cnt,tot,cur[N],Void[N];
struct Scapegoat {
	int Son[2],Exist,Val,Size,Fac;
}node[N];

inline bool balance(int x) {
    return (double)node[x].Fac*alpha>(double)max(node[node[x].Son[0]].Fac,node[node[x].Son[1]].Fac);
}

inline void read(int &x) {
	char ch=getchar();
	while (ch<'0' || ch>'9') {
		ch=getchar();
	}
	for (x=0;ch>='0' && ch<='9';ch=getchar()) {
		x=(x<<1)+(x<<3)+ch-'0';
	}
}

inline void write(int x) {
	if (x<0) {
		putchar('-');
		x=abs(x);
	}
	if (x>9) {
		write(x/10);
	}
	putchar(x % 10+'0');
}

inline void Init() {
    tot=0;
    for (register int i=N-1;i;--i) {
		Void[++tot]=i;
	}
}

inline void Build(int x) {
	node[x].Son[0]=node[x].Son[1]=0;
	node[x].Size=node[x].Fac=1;
}

inline void Insert(int &x,int val) {
	if (!x) {
		x=Void[tot--];
		node[x].Val=val;
		node[x].Exist=1;
		Build(x);
		
		return;
	}
	++node[x].Size;
	++node[x].Fac;
	if (val<=node[x].Val) {
		Insert(node[x].Son[0],val);
	}
	else {
		Insert(node[x].Son[1],val);
	}
}

inline void Traversal(int x) {
	if (!x) {
		return;
	}
	Traversal(node[x].Son[0]);
	if (node[x].Exist) {
		cur[++cnt]=x;
	}
	else {
		Void[++tot]=x;
	}
	Traversal(node[x].Son[1]);
}

inline void PushUp(int x) {
	node[x].Size=node[node[x].Son[0]].Size+node[node[x].Son[1]].Size+1;
	node[x].Fac=node[node[x].Son[0]].Fac+node[node[x].Son[1]].Fac+1;
}

inline void SetUp(int l,int r,int &x) {
	int mid=(l+r)>>1;
	x=cur[mid];
	if (l==r) {
		Build(x);
		
		return;
	}
	if (l<mid) {
		SetUp(l,mid-1,node[x].Son[0]);
	}
	else {
		node[x].Son[0]=0;
	}
	SetUp(mid+1,r,node[x].Son[1]);
	PushUp(x);
}

inline void ReBuild(int &x) {
	cnt=0;
	Traversal(x);
	if (cnt) {
		SetUp(1,cnt,x);
	}
	else {
		x=0;
	}
}

inline void check(int x,int val) {
    int s=val<=node[x].Val?0:1;
    while (node[x].Son[s]) {
        if (!balance(node[x].Son[s])) {
            ReBuild(node[x].Son[s]);
            return;
        }
        x=node[x].Son[s];
		s=val<=node[x].Val?0:1;
    }
}

inline int get_rank(int v) {
	int x=rt,rk=1;
	while (x) {
		if (node[x].Val>=v) {
			x=node[x].Son[0];
		}
		else {
			rk+=node[node[x].Son[0]].Fac+node[x].Exist;
			x=node[x].Son[1];
		}
	}
	
	return rk;
}

inline int get_val(int rk) {
	int x=rt;
	while (x) {
		if (node[x].Exist && node[node[x].Son[0]].Fac+1==rk) {
			return node[x].Val;
		}
		if (node[node[x].Son[0]].Fac>=rk) {
			x=node[x].Son[0];
		}
		else {
			rk-=node[x].Exist+node[node[x].Son[0]].Fac;
			x=node[x].Son[1];
		}
	}
}

inline void Delete_rank(int &x,int rk) {
	if (node[x].Exist && !((node[node[x].Son[0]].Fac+1)^rk)) {
		node[x].Exist=0;
		--node[x].Fac;
		
		return;
	}
	--node[x].Fac;
	if (node[node[x].Son[0]].Fac+node[x].Exist>=rk) {
		Delete_rank(node[x].Son[0],rk);
	}
	else {
		Delete_rank(node[x].Son[1],rk-node[x].Exist-node[node[x].Son[0]].Fac);
	}
}
inline void Delete_val(int v) {
	Delete_rank(rt,get_rank(v));
	if ((double)node[rt].Size*alpha>(double)node[rt].Fac) {
		ReBuild(rt);
	}
}



int main() {
	Init();
	read(n);
	for (;n;--n) {
        int opt,x;
        read(opt);
        read(x);
        switch (opt) {
            case 1:st=rt,Insert(rt,x),check(st,x);break;
            case 2:Delete_val(x);break;
            case 3:write(get_rank(x)),putchar('\n');break;
            case 4:write(get_val(x)),putchar('\n');break;
            case 5:write(get_val(get_rank(x)-1)),putchar('\n');break;
            case 6:write(get_val(get_rank(x+1))),putchar('\n');break;
        }
    }
    return 0;
}

后记
其实还是很容易理解的,稍微想一想,构一构图,替罪羊树就手打出来了
平衡树嘛,切多了可能就会了 吧

发布了27 篇原创文章 · 获赞 44 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_41221124/article/details/104032971