SPLAY(伸展树)学习笔记

splay简介

splay是一颗bst,有“序列之王”的美誉,可以处理序列的一切问题。处理区间只有splay和fhq_treap两棵平衡树(当然,如果你想写,B+)可以解决。

优点:万能、常数较小、可用于link cut tree

缺点:代码长、不能可持久化。

相关文章:fhq_treap link cut tree

splay操作

void splay(x,goal)

我们假设有一棵原树,长成这样:

splay树满足如下的性质:中序遍历是有序的(当处理普通平衡树问题的时候,且合法的操作都符合特性)。

注意:上述提醒对于序列操作无效,只是当你推导有困难的时候自己画图(脑补)考虑一下。

通过将节点旋转至根节点来进一步操作(称为splay操作)

那么我们先来看splay操作:

第一种情况、需旋转的节点x的父亲是目标节点p。那么只要一次旋转。如图x是左儿子,需要右旋。图为将a旋转到b。图片来自百度

第二种情况、x、x的父亲、x的父亲的父亲“三点共线”(如果不能理解就看下面的图),执行两次相同方向的旋转。那么先旋转x的父节点,再旋转x。图为将x点旋转到z点。图片来自百度。

第三种情况、else。执行两次不同方向的旋转。图为将x旋转到g。

上述三种情况介绍完之后,相信同学们对于旋转(还不懂?往下翻)以及splay操作有一定的认识。

递归写法:(来源:算法竞赛入门经典)

void splay(Node* &o,int k) {
    int d=o->cmp(k);
    if (d==1) k-=o->ch[0]->s+1;
    if (d!=-1) {
        Node *p=o->ch[d];
        int d2=p->cmp(k);
        int k2=(d2==0>k:k-p->ch[0]->s-1);
        if (d2!=-1) {
            splay(p->ch[d2],k2);
            if (d==d2) rotate(o,d^1); else rotate(o->ch[d],d);
        }
    }
    rotate(o,d^1);
}

非递归写法(建议同学们背一下):

void splay(int x,int goal=0) {
    for (int f;(f=fa[x])!=goal;rotate(x))
        if (fa[f]!=goal) rotate((get(x)==get(f))?f:x);
    if (!goal) rt=x;
}

void rotate(x)

就是上述过程中的旋转。需要完成下面4件事情:

X变到原来Y的位置
Y变成了 X原来在Y的 相对的那个儿子
Y的非X的儿子不变 X的 X原来在Y的 那个儿子不变
X的 X原来在Y的 相对的 那个儿子 变成了 Y原来是X的那个儿子

上代码。

bool get(int x) {return ch[fa[x]][1]==x;}
 
void rotate(int x) {
    int y=fa[x],z=fa[y],k=get(x); 
    //y是x的父亲,z是父亲的父亲,k是x是否是它父亲的右子树。 
    pushdown(y); pushdown(x); //不要忘记pushdown 
    ch[y][k]=ch[x][k^1]; fa[ch[y][k]]=y;
    ch[x][k^1]=y; fa[y]=x;
    fa[x]=z;
    if (z) ch[z][ch[z][1]==y]=x;
    pushup(y); pushup(x); //pushup 
}

未完待续

猜你喜欢

转载自www.cnblogs.com/JerryZheng2005/p/10323258.html
今日推荐