平衡树学习笔记

平衡树是平衡的二叉树,有很多种,比如:Splay,Treap,fhq Treap,红黑树,替罪羊树,AVL,SBT。种类很多,用法类似。所以这里只介绍Splay和fhq Treap。

Splay

基于rotate和splay操作。可以任意改变形状。

复杂度

复杂度证明

均摊每次操作 O ( l o g N ) O(logN) O(logN),但是常数较大。

基本操作

0. 变量定义

int cnt; // 点数
int root; // 根id
int fa[N], ch[N][2]; // 父亲和左右儿子
int val[N], sum[N]; // 每个点记录一些数据,这里以权值和区间和为例

1. rotate

摘抄一张图,来自santiego

在这里插入图片描述
看着图再对应一下代码就可以理解了

int son(int u){return ch[fa[u]][1] == u;}

void rotate(int u)
{
	int v = fa[u], w = fa[v];
	int ws = son(v), vs = son(u);
	ch[w][ws] = u;
	fa[v] = u;
	ch[v][vs] = ch[u][vs^1];
	fa[ch[u][vs^1]] = v;
	ch[u][vs^1] = v;
	fa[u] = w;
	push_up(v); push_up(u);
}

1.1

son(u):求u是fa[u]的哪个儿子,左儿子返回0,右儿子返回1。

push_up(u):更新区间信息

1.2 注意

修改 X X X一定要在最后,因为修改 c h [ D ] [ s o n ( X )    x o r    1 ] ch[D][son(X) \; xor\;1] ch[D][son(X)xor1]和修改 Y Y Y都需要用到他。

其他顺序随意。考场上画张图写出来还是轻松的。

2.splay

void splay(int u, int goal)
{
	while (fa[u] != goal){
		if (fa[fa[u]] != goal) rotate(son(u) == son(fa[u]) ? fa[u] : u);
		rotate(u);
	}
	if (goal == 0) root = u;
}

双旋。对我这种程度的选手需要记忆,因为理解的话需要会证Splay的均摊复杂度,人家证出来这么旋就是比较快也没有办法。

下面的图来自自为风月马前卒

2.1 一线型

在这里插入图片描述
先转父亲再转自己。转完还是一条线(看起来挺不科学的)。

2.2 弯曲型

在这里插入图片描述
转两次自己。转完是一个三角形。

总结下来就是:

一线型转父亲!!!

延伸操作

1 find

重要的辅助函数。

找到权值大于等于x的第一个点,并splay到根。

利用二叉查找树的性质,从根开始走一条链去找。

2 k_th

找第k大的数并splay到根。

与find相似,利用二叉查找树性质,从根开始往下爬。

3 insert

在第i个数后面插入一个或者一个序列的数。

find第i个数并splay到根。
在root的右子树中把所有节点都放到右半边,即把find右子树中的第1个数并splay到右子树的根。
这时root的右子树的左子树是空的,把要插的数放在这个位置即可。

4 delete

去掉一个区间的数。

和insert类似。

find两遍,把root的右子树的左子树整个去掉。

5 pre

寻找节点u的前驱。

把u节点splay到根。在左子树中一直往右儿子跑,直到跑不下去了为止,即为前驱。

6 suc

寻找节点u的后继。

与pre相似。

把u节点splay到根。在右子树中一直往左儿子跑,直到跑不下去了为止,即为后继。

7 reverse

翻转一个区间。

区间打标记(区间加减也是同理)。

两遍find把需要操作的区间放到root的右儿子的左儿子的位置,然后打上翻转标记,splay到根。

以后每次find的时候都需要先push_down()把翻转标记推下去。

fhq Treap

持续看心情更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值