FHQ-treap 小结

刚看了一天的平衡树,我就过来小结了......
只过了几道板子题,,,

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 <bits/stdc++.h>
#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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章