Scapegoat Tree
前言
以前觉得树形结构都难的要死,虽然现在也没好多少,但是明显对树形结构的了解要远超从前了。
平衡树什么的,以前总觉得遥不可及。什么 Treap 什么 Splay ,一听就很高端大气上档次啊,想必是及其优美的高级算法。
替罪羊树是一种较为简单的自平衡二叉搜索树,名字听起来还十分霸气,我想以替罪羊树作为跳板,揭开平衡树神秘的面纱。
以上200%为瞎扯)这也是瞎扯)
特点
一般的平衡树都依赖于旋转操作,而替罪羊树不一样(因为是重量平衡树),它选择及其优雅的方式维护性质。至于你问怎么个优雅法?暴力即是优雅!
仔细想来,替罪羊树似乎与BST有几分相像,因为是平衡树, 做为一棵平衡树,它会在子树失去平衡后暴力重构以保证子树满足平衡树的性质(定义一个平衡树因子
α
\alpha
α ,对替罪羊树的每个节点
t
t
t,都需要满足:
max
(
s
i
z
e
l
,
s
i
z
e
r
)
⩽
α
×
s
i
z
e
t
\max\rm(size_l,size_r) \leqslant \alpha\times size_t
max(sizel,sizer)⩽α×sizet)。
时间复杂度
可以保证,除重构外,所有的操作的平摊最坏时间复杂度为 O ( log n ) O(\log n) O(logn),而重构是 O ( n ) O(n) O(n),但均摊下来也是 l o g log 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);
}
}
//删除排名为rank的数
inline void Delete_rank(int &x,int rk) {
//如果当前结点存在且排名为rank,删除结点
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) {
//判断当前元素是否大于val,是就访问左子数,否则累加当前元素排名,访问右子树
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) {
//若当前元素排名为rank则返回,否则判断当前元素的排名是否小于rk,决定访问左子数,或访问右子树,rank减去当前元素排名
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];
}
}
}
随便举个例子:
显然不合法,那就把它压扁,
然后吊起来挂着,
操作就完成了。
//压扁树
#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 ScapegoatTree {
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;
}
后
记
后记
后记
其实还是很容易理解的,稍微想一想,构一构图,替罪羊树就手打出来了
平衡树嘛,切多了可能就会了 吧