Splay笔记

DS都快忘了。

分块学习笔记不会再写,只会再写别的。

平衡树

众所周知,BST很方便,就是会退化成链或树很高,所以我们需要更高效的DS,那就是——平衡树。

Splay

Splay好闪,拜谢Splay。

Splay是通过不断将某个节点旋转至根来保持平衡,同时满足BST的性质。

怎么旋转呢?我们来画两张图!

左旋

这是一张图:

经过左旋后变为:

可以看出就是将 2 2 2 的右儿子 1 1 1 变为 2 2 2 的父亲,然后把 1 1 1 的左儿子变成了 2 2 2 的右儿子(也就是变为 1 1 1 的孙子)。

右旋

和左旋相反,下边给出图示(其实就是图片倒了一下):

经过右旋后变为:

同样可以看出,就是将 1 1 1 的左儿子 2 2 2 变为 1 1 1 的父亲,然后把 2 2 2 的右儿子变成了 1 1 1 的左儿子(也就是变为 2 2 2 的孙子)。

其实就是相反的

某个节点的旋转就是这样的:

  • 如果他是某个节点的左儿子,就右旋。

  • 如果他是某个节点的右儿子,就左旋。


但是旋一次只会和父亲交换而不是到根,咋整?

答案是暴力。

是的,你没看错,只需要暴力转就可以了。

那不就写完了?溜了溜了

别急!这是单旋Splay,它在棺材里躺得好好的呢!

单旋Splay很容易被卡到链。

如下图,如果有三点共线的情况,那么最后跳来跳去还是一条链,那咋办?

先转 2 2 2 再转 3 3 3 即可。

然后我们也可以得出规律了,就是先转父亲,再转自己一直这样直到自己变成根。

下面借一下大佬Enoch006的图,就是这样修改的:

接下来讲代码和操作啦。

先来声明一下变量捏。

siz//代表整棵Splay的大小
root//Splay的根节点
sz[i]//i的子树的大小
num[i]//i这个节点的值
cnt[i]//i这个节点的值出现的次数
fa[i]//i的父亲
son[i][0]//i的左儿子
son[i][1]//i的右儿子

清空

这个操作在删除后执行。

void all_zero(int x){
	fa[x]=0;
	son[x][0]=0;
	son[x][1]=0;
	num[x]=0;
	cnt[x]=0;
	sz[x]=0;
}

get

判断当前的点是左儿子还是右儿子(旋转要用)。

int get(int x){
	if(x==son[fa[x]][0]){
		return 0;
	}return 1;
}

pushup

随便写的名字

用于修改后确认并修改树的大小。

void pushup(int x){
	if(x>=1){
		sz[x]=cnt[x];
		if(son[x][0]>=1){
			sz[x]+=sz[son[x][0]];
		}if(son[x][1]>=1){
			sz[x]+=sz[son[x][1]];
		}
	}
}

rotate

如下图。

我们要旋转 4 4 4,就会变成下面的:

一开始 4 4 4 的父亲是 2 2 2 2 2 2 的父亲是 1 1 1

flagget(4),那么就是 4 4 4 是左儿子还是右儿子。

那么可以发现, 2 2 2flag 儿子和 4 4 4flag^1 儿子还有 1 1 1 的或左或右的儿子(就是看一开始 2 2 2 是哪个儿子)都要换。

其实旋转就是连边、断边。

(下面的数字就是上面那棵树的节点)。

于是我们先把 2 2 2 6 6 6 连上,再连 4 4 4 2 2 2,最后连 1 1 1 2 2 2

那么旋转就好了。

void rotate(int x){
	int f=fa[x],flag=get(x);
	int gra=fa[f];
	son[f][flag]=son[x][flag^1];
	fa[son[f][flag]]=f;
	son[x][flag^1]=f;
	fa[f]=x,fa[x]=gra;
	if(gra>=1){
		if(son[gra][1]==f){
			son[gra][1]=x
		}else{
			son[gra][0]=x
		}
	}pushup(f),pushup(x);
}

Splay

双旋的关键。

这里判断两种情况:

  • 三点一线,这样就是上面说的,先转父亲,再转自己一直这样直到自己变成根。

  • 不是三点一线,疯狂暴力旋转即可。

void Splay(int x){
	for(int f=0,f=fa[x];rotate(x)){
		if(fa[f]>=1){
			if(get(x)==get(fa)){
				rotate(fa);
			}else{
				rotate(x);
			}
		}
	}rt=x;
}

咕了。

  • 34
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可持久化splay是一种数据结构,它是对splay树进行修改和查询的一种扩展。在传统的splay树中,对树的修改操作会破坏原有的树结构,而可持久化splay树则允许我们对树进行修改、查询,并且可以保存修改后的每个版本的树结构。 在可持久化splay树中,我们不会直接对原树进行修改,而是通过复制每个节点来创建新的版本。这样,每个版本都可以独立地修改和查询,保留了原有版本的结构和状态。每个节点保存了其左子树和右子树的引用,使得可以在不破坏原有版本的情况下进行修改和查询。 为了实现可持久化splay树,我们可以使用一些技巧,比如引用中提到的哨兵节点和假的父节点和孩子节点。这些技巧可以帮助我们处理根节点的旋转和其他操作。 此外,可持久化splay树还可以与其他数据结构相结合,比如引用中提到的可持久化线段树。这种结合可以帮助我们解决更复杂的问题,比如区间修改和区间查询等。 对于可持久化splay树的学习过程,可以按照以下步骤进行: 1. 理解splay树的基本原理和操作,包括旋转、插入、删除和查找等。 2. 学习如何构建可持久化splay树,包括复制节点、更新版本和保存历史版本等。 3. 掌握可持久化splay树的常见应用场景,比如区间修改和区间查询等。 4. 深入了解与可持久化splay树相关的其他数据结构和算法,比如可持久化线段树等。 在解决问题时,可以使用二分法来确定答案,一般称为二分答案。通过对答案进行二分,然后对每个答案进行检查,以确定最终的结果。这种方法可以应用于很多问题,比如引用中提到的在线询问问题。 综上所述,可持久化splay是一种对splay树进行修改和查询的扩展,可以通过复制节点来创建新的版本,并且可以与其他数据结构相结合解决更复杂的问题。学习过程中可以按照一定的步骤进行,并且可以使用二分法来解决一些特定的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [[学习笔记]FHQ-Treap及其可持久化](https://blog.csdn.net/weixin_34283445/article/details/93207491)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [可持久化数据结构学习笔记](https://blog.csdn.net/weixin_30376083/article/details/99902410)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值