优雅的暴力:替罪羊树
创作时间:
作者:
@小白创作中心
优雅的暴力:替罪羊树
引用
1
来源
1.
https://www.luogu.com.cn/article/xlvxb1h6
替罪羊树是一种自平衡二叉搜索树,通过懒惰删除和重构机制来保持树的平衡。它在插入和删除操作时,不会立即进行复杂的平衡调整,而是等到树的不平衡程度达到一定程度时才进行重构,这种设计使得替罪羊树在某些场景下具有较高的效率。
本文无大错误不再更新,会更新在博客。
默认读者会 BST 的基本操作。
节点定义
替罪羊树采用了懒惰删除的方法,不会立即删除某个点,而是在重构时不放进数组。
struct node{
int ch[2], val;
int siz1, siz2, cnt, sum;
//扣去懒惰删除的节点数量,没扣去懒惰删除的节点数量,树内相同权值的数量,子树大小。
}d[N];
int root, tot, stk[N], top, v[N], t;//stk 是垃圾回收
double al = 0.75;
int newnode(int x){
int w = top ? stk[top--] : ++tot;
return d[w].val = x, ls(w) = rs(w) = 0, d[w].cnt = 1, pushup(w), w;
}
void pushup(int x){
node&rt = d[x],ls = d[ls(x)],rs = d[rs(x)];
rt.siz1 = (rt.cnt > 0) + ls.siz1 + rs.siz1;
rt.siz2 = 1 + ls.siz2 + rs.siz2;
rt.sum = rt.cnt + ls.sum + rs.sum;
}
重构
BST 最担心的是树退化成链。
那么有个暴力的想法:
把树拍扁放进数组,然后重新构建一棵完全二叉树。
但是过多的重构会使复杂度上升,那么我们引入一个概念:
一般的平衡树都能把 维护在 左右。
我们可以将 设为一个数,一般为 。
一般选 。
在某个节点的 时,我们把这个子树重构。
如果这个树 ,那么我们认为它也是需要重构的。
比如这棵树:
那么我们将它拍扁放进数组。
然后像线段树一样重新建树。
void dfs(int x){
if(!x)return;
dfs(ls(x)), (d[x].cnt ? v[++t] : stk[++top]) = x, dfs(rs(x));
}
int build(int l, int r){
if(l == r)return ls(v[l]) = rs(v[l]) = 0, pushup(v[l]), v[l];
if(l > r)return 0;
int mid = l + r >> 1, x = v[mid];
ls(x) = build(l, mid - 1), rs(x) = build(mid + 1, r);
return pushup(x), x;
}
插入
如果在当前节点的权值和要插入的权值一样,我们将 增加。
其他和 BST 一样。
记得在回溯时更新节点,判断是否重构。
void insert(int&now, int val){
if(!now)return void(now = newnode(val));
if(d[now].val == val)d[now].cnt++;
else if(d[now].val < val)insert(rs(now), val);
else insert(ls(now), val);
pushup(now);
if(check(now))refactoring(now);
}
删除
懒惰删除,只是将 减少。
然后在回溯时更新节点,判断是否重构。
void del(int&now, int val){
if(!now)return;
if(d[now].val == val)d[now].cnt--;
else if(d[now].val < val)del(rs(now), val);
else del(ls(now), val);
pushup(now);
if(check(now))refactoring(now);
}
查询操作
这部分就差不多了。
int kth(int x){
int now = root, siz = 0, z = x;
while(now){
if((siz = d[ls(now)].sum) >= x)now = ls(now);
else if((siz += d[now].cnt) < x)x -= siz, now = rs(now);
else return d[now].val;
}
return -1;
}
int query_rank(int val){
int ans = 1, now = root;
while(now){
if(d[now].val == val)ans += d[ls(now)].sum, now = 0;
else if(d[now].val < val)ans += d[ls(now)].sum + d[now].cnt, now = rs(now);
else now = ls(now);
}
return ans;
}
int ask_pre(int val){return kth(query_rank(val) - 1);}
int ask_next(int val){return kth(query_rank(val + 1));}
代码
完整代码。
- 优雅的暴力:替罪羊树
- 前言
- 节点定义
- 重构
- 插入
- 删除
- 查询操作
- 代码
热门推荐
《哪吒2》凭何一骑绝尘?|南方深读
我国法律规定如何请病假理由比较好?
太阳能发电系统小型化,家庭与社区的绿色能源解决方案
熊胆真的“不可替代”吗?
INFJ 配偶特点及伴侣配对指南
鼻窦炎的药物治疗方案
西放百家|美洲大蠊抗肿瘤活性及其在恶性肿瘤防治中的应用前景
每天坚持喝黑咖啡真的能减肥?医生:或会带来“三种风险”,小心身体受不了!
美国:上帝之杖到底行不行?中国:试过了,不太行
QQ三国JS技能快速升级攻略
吃进去的东西,隔天排出算正常?这个时间很重要!肠道健康自测方法来了
螨虫的危害,你知道吗?
显卡怎么看型号?全面解析显卡型号识别方法
为什么凯撒被称为“大帝”,而罗马帝国首位皇帝渥大维无此殊荣?
罗医消化:从慢性萎缩性胃炎到胃癌要多久?
吃点藕叭,对心脏和肠胃有益的那种🤗
小孩需要几岁可以开始刷牙?来看下如何正确护理儿童口腔健康
北欧神话中的十二主神:神秘力量的象征与传承
怀孕期间的四大关键指标:体重、血压、血糖和胎动的正常范围
吃苹果的好处有哪些?了解为健康加分的奥秘
如何通过劳务外包降低企业成本?
关于汽油标号即辛烷值的问题
动漫中的爱与陪伴,悠然的时光之旅
讨好型人格是心理疾病吗?
现代艺术最重要的12种技法
婴儿低烧反复发烧的原因
10万元门槛真相:开通创业板到底需要现金还是股票?过来人教你3个避坑技巧
东澳岛旅游攻略:交通指南、船票价格、游玩路线全解析
东澳岛旅游攻略:交通指南、船票价格、游玩路线全解析
Ctrl+Q是什麼?功能解析與使用技巧