Splay教程

前言

Splay是名副其实的区间小能手。它会经常出现在一些有关区间的题上。而本蒟蒻只会Treap,感到分外难受,于是就有了这个教程。

引入

Splay首先是一颗二叉查找树。也就是说,对于任何一颗子树,左子树内所有的值均小于根,右子树内所有的值均大于根。也就是下图这样,数字代表值。

1

这样的话,中序遍历的结果就是从小到大的。

但是这棵树可能会退化成这样:

2

会使操作奇慢无比。所以我们考虑优化。

教程

Rotate

Rotate操作能在不破坏二叉查找树的性质的情况下更改树的结构:

3

感受一下,如果我们用\(Child[0]\)表示左孩子,\(Child[1]\)表示右孩子,那么Rotate就可以这样写:

void Rotate( int C ) {
    int B = Father[ C ];
    int A = Father[ B ];
    int Tag = Child[ B ][ 1 ] == C;
    Child[ A ][ Child[ A ][ 1 ] == B ] = C;
    Father[ C ] = A;
    Child[ B ][ Tag ] = Child[ C ][ Tag ^ 1 ];
    Father[ Child[ C ][ Tag ^ 1 ] ] = B;
    Child[ C ][ Tag ^ 1 ] = B;
    Father[ B ] = C;
    return;
}

Splay

Splay操作是核心。Splay( x, y )表示把\(x\)旋转到\(y\)的儿子,Splay( x, 0 )就是把\(x\)旋转到根。或许不难想到这样的操作:

while( Father[ x ] != y ) Rotate( x );

但是这样有一个问题:

对于下图

4

如果我们Splay( C, 0 ),按照上面的做法,就会变成这样:

11

我们发现链\(A-B-C-y\)依旧存在,只是变成了\(C-A-B-y\)。这样的话时间复杂度就无法保证了。

这种情况只有在C是B的左孩子,B是A的左孩子,或者C是B的右孩子,B是A的右孩子的情况下才会发生。这时,我们考虑先Rotate(B),然后Rotate(C)。大家可以画图感受一下。

所以Splay可以这样写:

void Splay( int Index, int Goal ) {
    while( Father[ Index ] != Goal ) {
        int B = Father[ Index ]; 
        int A = Father[ B ];
        if( A != Goal ) 
            if( ( Child[ A ][ 0 ] == B ) ^ ( Child[ B ][ 0 ] == Index ) ) 
                Rotate( Index );
            else
                Rotate( B );
        Rotate( Index );
    }
    return;
}

这样就好了啊!

一些其他操作:

插入、删除、查询数x的排名、查询排名第x的数、查询前驱、查询后缀等都与Treap相似,只要最后把目标线Splay到根就行,这里不再赘述。

区间翻转

下面通过文艺平衡树一题来讲述如何翻转区间。

这一题中,关键字是位置。

假设我们要翻转\([l,r]\)。我们首先将\(l-1\)旋转到根,再将\(r+1\)转到根的儿子,这样根的右孩子的左孩子就是\([l,r]\)了。我们打上标记就可以了。

结语

先简略地介绍这么一点,剩下的大家可以慢慢体会。

转载于:https://www.cnblogs.com/chy-2003/p/10463118.html

Splay删除子树是一种数据结构和算法中的操作,通常用于自平衡二叉搜索树(如AVL、红黑树或Treap)的变种——Splay Tree中。Splay Tree是一种动态查找树,其特点是每次访问后都会对节点进行旋转操作(splaying),使其最近被访问的节点处于根部。 当要删除一个节点时,在常规的二叉搜索树中,我们需要找到该节点并删除它,然后处理可能由删除引起的不平衡。在Splay Tree中,这个过程有所不同: 1. **查找子树**: 首先,我们在树中寻找指定的子树,这可以通过标准的查找算法实现,同时保持对父节点的更新。 2. **Splay节点**: 找到子树后,我们对包含目标节点的路径上的所有节点执行一系列旋转操作(可能是单旋转或双旋转),直到目标节点到达根部。这个过程确保了频繁访问的路径被高效地访问。 3. **删除目标节点**: 当目标节点处于根部时,删除操作变得相对简单。如果目标节点有两颗子树,则替换为其右孩子的最小值或左孩子的最大值(取决于树的类型)。如果只有一个子树,那么就直接删除。 4. **重新平衡** (可选): 取决于Splay Tree的具体实现,可能会有一个额外的步骤来确保整棵树的平衡,但这不是必须的,因为Splay已经尽可能地减少了不平衡的可能性。 Splay删除子树的时间复杂度通常是O(log n),n是树的大小,因为每个旋转操作最多改变一棵高度为h的树的高度至h+1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值