刚看了一天的平衡树,我就过来小结了......
只过了几道板子题,,,
FHQ-treap的基本原理
基本原理就是用分裂(split)和合并(merge)的方式完成平衡树的操作
两种基本操作及其原理
好像我也不太懂 ...wtcl
split 分裂 的基本原理
Split(rt,c,&x,&y) 将以rt节点为根的子树按 参数 c 分裂为两颗子树 x, y,并返回其编号。
以按权值分裂为例,称权值较小的树为左树,另一棵为右树。
根据二叉搜索树左小右大的性质,可确定分裂的过程:
比较根节点权值 与 给定权值的大小关系
若小于给定权值,则根及左子树点都小于等于给定权值,将其归入左树中。向右子树递归,继续分裂子树。
否则,根及右子树点都大于等于给定权值,将其归入右树中。向左子树递归,继续分裂子树。
递归到叶子节点退出.
split code
void split(int rt, int c, int &x, int &y) {
if (!rt) {
x = y = 0;
return;
}
if (tree[rt].val <= c) x = rt, split(tree[rt].r, c, tree[rt].r, y);
else y = rt, split(tree[rt].l, c, x, tree[rt].l);
push_up(rt);
}
merge 合并的基本思路
merge(x,y) 将以x,y节点为根的两棵树合并为一棵,并返回新树根的编号。
特别注意:合并的两棵树 一定是 Split 分裂获得的两棵树
而且只要merge出现,前边一定有一个split, 而且合并的子树一定是split分裂的树
merge code
int merge(int x, int y) {
if (!x || !y) return x + y;
if (tree[x].rand < tree[y].rand) {
tree[x].r = merge(tree[x].r, y), push_up(x);
return x;
} else {
tree[y].l = merge(x, tree[y].l), push_up(y);
return y;
}
}
基于分离和合并的平衡树操作
插入一个数
我们先把树分为x,y两部分,然后把新的节点a看做是一棵树,先与x合并,合并完之后将合并的整体与y合并
void insert(int c) {
split(root, c, tmp1, tmp2);
root = merge(merge(tmp1, newnode(c)), tmp2);
}
删除一个数
首先我们把树分为x和z两部分
那么x树中的最大权值为a
再把x分为x和y两部分。
此时x中的最大权值为a-1,且权值为a的节点一定是y的根节点。
然后我们可以无视y的根节点,直接把y的左右孩子合并起来,这样就成功的删除了根节点,
最后再把x,y,z合并起来就好
void shan(int x) {
split(root, x, tmp1, tmp3), split(tmp1, x - 1, tmp1, tmp2);
tmp2 = merge(tree[tmp2].l, tree[tmp2].r);
root = merge(merge(tmp1, tmp2), tmp3);
}
查询a的排名
我们首先按照a-1的权值把树分开。
那么x树中最大的应该是a-1。
那么a的排名就是siz[x]+1
void query_rank(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[tmp1].size + 1);
root = merge(tmp1, tmp2);
}
查询排名为a的数
直接调用查找排名的函数即可,
这个函数应该比较好理解。
printf("%d\n", tree[kth(root, x)].val);
求x的前驱(前驱定义为小于a,且最大的数)
因为要小于a,那么我们按照a-1的权值划分,
x中最大的一定是<=a-1的,
所以我们直接输出x中最大的数就好,
(这里有一个小技巧,因为siz储存的是节点的数目,然后根据二叉查找树的性质,编号最大的就是值最大的)
void query_pre(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[kth(tmp1, tree[tmp1].size)].val);
root = merge(tmp1, tmp2);
}
求x的后继(后继定义为大于x,且最小的数)
和上一个原理类似
void query_suc(int x) {
split(root, x, tmp1, tmp2);
printf("%d\n", tree[kth(tmp2, 1)].val);
root = merge(tmp1, tmp2);
}
code
#include
#define ll long long
#define N 100010
#define M 1010
using namespace std;
int n, tmp1, tmp2, tmp3, add_node, root;
struct node {
int l, r, size, val, rand;
}tree[N];
int read() {
int s = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
void push_up(int rt) {
tree[rt].size = tree[tree[rt].l].size + tree[tree[rt].r].size + 1;
}
int newnode(int c) {
add_node++;
tree[add_node].val = c, tree[add_node].size = 1;
tree[add_node].rand = rand();
return add_node;
}
void split(int rt, int c, int &x, int &y) {
if (!rt) {
x = y = 0;
return;
}
if (tree[rt].val <= c) x = rt, split(tree[rt].r, c, tree[rt].r, y);
else y = rt, split(tree[rt].l, c, x, tree[rt].l);
push_up(rt);
}
int merge(int x, int y) {
if (!x || !y) return x + y;
if (tree[x].rand < tree[y].rand) {
tree[x].r = merge(tree[x].r, y), push_up(x);
return x;
} else {
tree[y].l = merge(x, tree[y].l), push_up(y);
return y;
}
}
int kth(int now, int key) {
while (1) {
if (key <= tree[tree[now].l].size) {
now = tree[now].l;
continue;
}
if (key == tree[tree[now].l].size + 1) return now;
key -= tree[tree[now].l].size + 1;
now = tree[now].r;
}
}
void insert(int c) {
split(root, c, tmp1, tmp2);
root = merge(merge(tmp1, newnode(c)), tmp2);
}
void shan(int x) {
split(root, x, tmp1, tmp3), split(tmp1, x - 1, tmp1, tmp2);
tmp2 = merge(tree[tmp2].l, tree[tmp2].r);
root = merge(merge(tmp1, tmp2), tmp3);
}
void query_rank(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[tmp1].size + 1);
root = merge(tmp1, tmp2);
}
void query_pre(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[kth(tmp1, tree[tmp1].size)].val);
root = merge(tmp1, tmp2);
}
void query_suc(int x) {
split(root, x, tmp1, tmp2);
printf("%d\n", tree[kth(tmp2, 1)].val);
root = merge(tmp1, tmp2);
}
int main() {
n = read();
for (int i = 1; i <= n; i++) {
int opt = read(), x = read();
if (opt == 1) insert(x);
if (opt == 2) shan(x);
if (opt == 3) query_rank(x);
if (opt == 4) printf("%d\n", tree[kth(root, x)].val);
if (opt == 5) query_pre(x);
if (opt == 6) query_suc(x);
}
}
几道有手就会做的题
洛谷P3369
洛谷P3871
洛谷P2343