以前觉得树形结构都难的要死,虽然现在也没好多少,但是明显对树形结构的了解要远超从前了
替罪羊树是我偷听dalao对话时听来的数据结构,总觉得遥不可及,还十分霸气 其实似乎没有那么深奥,我想以替罪羊树作为跳板,撕开 平衡树 神秘的面纱
以上200%为瞎扯)这也是瞎扯)
仔细想来,替罪羊树似乎与BST有几分相像,做为一棵平衡树,它会在子树失去平衡后 暴力重构
以保证子树满足平衡树的性质(定义一个平衡树因子
,对替罪羊树的每个节点
,都需要满足:
),而不是一般平衡树的旋转操作
可以保证,除重构外,所有的操作的平摊最坏时间复杂度为
,而重构是
,但均摊下来也是
级别,在可接受范围之内
//插入元素
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);
}
}
//删除排名为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的数的排名
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;
}
其实还是很容易理解的,稍微想一想,构一构图,替罪羊树就手打出来了
平衡树嘛,切多了可能就会了 吧