普通平衡树
题目大意
平衡树模板题,要求维护一些操作。
插入一个数,删除一个数,查询一个数的排名,查询排名一直的数,找前驱后继。
思路
这个其实是模板题,可以有各种解法。
现在在学替罪羊树,就用替罪羊树来做了。
后面有空的话应该会弄一个平衡树合集。
替罪羊其实就是弄一个限度,如果对于一个点,它的某个子树的点的个数占了这个点对应子树点个数的一定量的时候,就需要暴力重构它对应的树,把它的深度弄小。
弄成什么最小呢?没错,就是尽可能弄成每个点两个子树的个数相同的情况。
对于看是否要重构的那个量,我们一般会设 0.6∼0.7。
然后就来看看如何暴力重构。
首先,它就要两个子树大小相同,那假设你还原出来数组,那就是每次折半,前面的给左子树,后面的给右子树。
那怎么找到子树呢?我们可以用中序遍历,因为平衡树有二叉搜索树的性质。
然后你再按先序遍历把重构的树里面的点都找出来,然后你每次折半找子树的时候,就拿出一个来作为这个子树的根。
然后其他操作就很暴力,就不多说了,看代码吧。
代码
#include<cstdio>
#define alpha 0.7
using namespace std;
struct Tree {
int l, r, val, fa, num, sum;
}tree[1000001];
struct reTree {
int val, num;
}retree[100001];
int n, op, x, root, sta[100001];
int maxn, tot, tott;
bool check(int x) {
if (!tree[x].num) return 0;
if (tree[tree[x].l].sum >= (double)(alpha * tree[x].sum)) return 1;
if (tree[tree[x].r].sum >= (double)(alpha * tree[x].sum)) return 1;
return 0;
}
void up(int now) {
tree[now].sum = tree[now].num;
if (tree[now].l) tree[now].sum += tree[tree[now].l].sum;
if (tree[now].r) tree[now].sum += tree[tree[now].r].sum;
}
void dfs(int x) {
sta[++sta[0]] = x;
if (tree[x].l) dfs(tree[x].l);
if (tree[x].num) {
retree[++tott].val = tree[x].val;
retree[tott].num = tree[x].num;
}
tree[x].sum = 0;
if (tree[x].r) dfs(tree[x].r);
}
int get_new() {
if (sta[0]) {
sta[0]--;
return sta[sta[0] + 1];
}
else return ++tot;
}
void make_new(int l, int r, int now, int father) {
int mid = (l + r) >> 1;
tree[now].fa = father;
tree[now].num = retree[mid].num;
tree[now].val = retree[mid].val;
tree[now].sum = 1;
if (l <= mid - 1) {
tree[now].l = get_new();
make_new(l, mid - 1, tree[now].l, now);
}
else tree[now].l = 0;
if (r >= mid + 1) {
tree[now].r = get_new();
make_new(mid + 1, r, tree[now].r, now);
}
else tree[now].r = 0;
up(now);
}
void rebuild(int x) {
if (!x) return ;
tott = 0;
dfs(x);
int now = get_new();
if (x == root) root = now;
tree[now].fa = tree[x].fa;
int mid = (1 + tott) >> 1;
if (retree[mid].val < tree[tree[now].fa].val) tree[tree[now].fa].l = now;
else tree[tree[now].fa].r = now;
make_new(1, tott, now, tree[now].fa);
}
void insert(int x, bool need_build) {
if (root == 0) {
root = 1;
tot = 1;
tree[1] = (Tree){0, 0, x, 0, tree[1].num + 1, tree[1].sum + 1};
return ;
}
int now = root;
while (tree[now].sum) {
if (x == tree[now].val) {
tree[now].num++;
tree[now].sum++;
return ;
}
if (!tree[now].l && x < tree[now].val) break;
if (!tree[now].r && x > tree[now].val) break;
tree[now].sum++;
if (x < tree[now].val) now = tree[now].l;
else if (x > tree[now].val) now = tree[now].r;
}
if (x == tree[now].val) {
tree[now].num++;
tree[now].sum++;
return ;
}
tree[now].sum++;
int tmp = now;
if (x < tree[now].val) now = tree[now].l = ++tot;
else if (x > tree[now].val) now = tree[now].r = ++tot;
tree[now].num++;
tree[now].sum++;
tree[now].val = x;
if (tmp != now) tree[now].fa = tmp;
int first_fix = 0;
while (now != root) {
now = tree[now].fa;
if (check(now)) first_fix = now;
}
if (need_build && first_fix) rebuild(first_fix);
}
int tree_place(int x) {
int now = root;
while (tree[now].val != x && tree[now].sum != 0) {
if (x <= tree[now].val) now = tree[now].l;
else now = tree[now].r;
}
return now;
}
void delete_(int x, int need_build) {
tree[x].num--;
tree[x].sum--;
int first_fix = 0;
while (x != root) {
x = tree[x].fa;
tree[x].sum--;
if (check(x)) first_fix = x;
}
if (need_build && first_fix) rebuild(first_fix);
}
int get_rank(int x) {
insert(x, 0);
int now = tree_place(x);
int re = tree[tree[now].l].sum + 1;
while (now != root) {
if (tree[tree[now].fa].r == now)
re += tree[tree[tree[now].fa].l].sum + tree[tree[now].fa].num;
now = tree[now].fa;
}
delete_(tree_place(x), 0);
return re;
}
int get_num(int x) {
int now = root;
while (x) {
if (x <= tree[tree[now].l].sum) now = tree[now].l;
else if (x <= tree[tree[now].l].sum + tree[now].num) return tree[now].val;
else {
x -= tree[tree[now].l].sum + tree[now].num;
now = tree[now].r;
}
}
return tree[now].fa;
}
int pre(int x) {
insert(x, 0);
int need_rank = get_rank(x) - 1;
delete_(tree_place(x), 0);
return get_num(need_rank);
}
int suc(int x) {
insert(x + 1, 0);
int need_rank = get_rank(x + 1);
delete_(tree_place(x + 1), 0);
return get_num(need_rank);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &op, &x);
if (op == 1) {
insert(x, 1);
continue;
}
if (op == 2) {
delete_(tree_place(x), 1);
continue;
}
if (op == 3) {
printf("%d\n", get_rank(x));
continue;
}
if (op == 4) {
printf("%d\n", get_num(x));
continue;
}
if (op == 5) {
printf("%d\n", pre(x));
continue;
}
if (op == 6) {
printf("%d\n", suc(x));
continue;
}
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现