伸展树 Splay 模板

学习Splay的时候参考了很多不同的资料,然而参考资料太杂的后果就是模板调出来一直都有问题,尤其是最后发现网上找的各种资料均有不同程度的错误。

好在啃了几天之后终于算是啃下来了。

Splay也算是平衡树的一种,但是跟AVL树、SBT不同的是,Splay并不是一直保持严格的平衡,因此在速度上可能要慢一些,但是统计学上仍能保证Splay具有O(logn)的均摊复杂度。

Splay原生不需要附加任何空间,它的先天优势是其特有的Splay操作可以非常灵活地改变整棵树的结构形态,完成一般线段树、平衡树做不到的任务。

  • 基本操作-左右旋

Splay的基本操作与其他平衡树相似,都是进行结点的左右旋转。与之前写的SBT相比,Splay除了左右儿子域以外还需要一个father域,这个是因为下面的Splay操作需要用到父节点来判断当前的形态。

  • 核心操作-splay

Splay(x,y)的作用是将当前点x旋转到y的子结点上,一般写y=0时则将x旋转到整棵树的根。这个操作在splay的所有函数中基本上最后都要加上,简单的理解就是将常用的结点尽可能地上移到深度较浅的地方,据统计若去掉函数最后对操作结点的splay,则整个程序将会慢上数倍。

splay基本的想法是根据当前结点父节点形态的不同,分成四种情况,逐步将结点旋转到根上。

  • 进阶操作

插入、查找值、查找第k个等等...基本与普通的二叉查找树相似,但是记得在最后要加上splay,将使用过的结点旋转到根。

特殊的删除操作:

splay本身是一棵二叉搜索树,可以进行一般的删除操作。另外,二叉搜索树的性质配合splay的操作可以做到O(logn)动态删除一整个区间的结点,而一般的平衡树则只能一个一个删除。

例如:删除大于l且小于r的数,先将序号小于l的最大结点伸展到根,序号大于r的最小的结点伸展到根的右子树,则此时,r的后继结点的左子树中就存放着从l到r的所有结点,此时只要把这棵子树整个断开即可。

............

/* ***********************************************
MYID    : Chen Fan
LANG    : G++
PROG    : Splay Tree
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define MAXN 100000

int sons[MAXN][2];
int father[MAXN],size[MAXN],data[MAXN];
int spt=0,spttail=0;

void rotate(int x,int w) //rotate(node,0/1)
{
    int y=father[x];
    sons[y][1-w]=sons[x][w];
    if (sons[x][w]) father[sons[x][w]]=y;

    father[x]=father[y];
    if (father[y])
    if (y==sons[father[y]][0]) sons[father[y]][0]=x;
    else sons[father[y]][1]=x;

    sons[x][w]=y;
    father[y]=x;

    size[x]=size[y];
    size[y]=size[sons[y][0]]+size[sons[y][1]]+1;
}

void splay(int x,int y) //splay(node,position)
{
    if (!x) return ;
    while(father[x]!=y)
    {
        if (father[father[x]]==y)
            if (x==sons[father[x]][0]) rotate(x,1);
            else rotate(x,0);
        else
            if (father[x]==sons[father[father[x]]][0])
                if (x==sons[father[x]][0])
                {
                    rotate(father[x],1);
                    rotate(x,1);
                } else
                {
                    rotate(x,0);
                    rotate(x,1);
                }
            else
                if (x==sons[father[x]][1])
                {
                    rotate(father[x],0);
                    rotate(x,0);
                } else
                {
                    rotate(x,1);
                    rotate(x,0);
                }
    }
    if (!y) spt=x;
}

void search(int x,int w)
{
    while(data[x]!=w)
    {
        if (w<data[x])
        {
            if (sons[x][0]) x=sons[x][0];
            else break;
        } else if (w>data[x])
        {
            if (sons[x][1]) x=sons[x][1];
            else break;
        }
    }
    splay(x,0);
}

void insert(int w) //insert(value)
{
    spttail++;
    data[spttail]=w;
    size[spttail]=1;
    sons[spttail][0]=0;
    sons[spttail][1]=0;
    if (!spt)
    {
        father[spttail]=0;
        spt=spttail;
    } else
    {
        int x=spt;
        while(1)
        {
            size[x]++;
            if (w<data[x])
                if (sons[x][0]) x=sons[x][0];
                else break;
            else
                if (sons[x][1]) x=sons[x][1];
                else break;
        }
        father[spttail]=x;
        if (w<data[x]) sons[x][0]=spttail;
        else sons[x][1]=spttail;
        splay(spttail,0);
    }
}

void select(int x,int v) //select(root,k)
{
    while(v!=size[sons[x][0]]+1)
    {
        if (v<=size[sons[x][0]]) x=sons[x][0];
        else
        {
            v-=size[sons[x][0]]+1;
            x=sons[x][1];
        }
    }
    splay(x,0);
}

int succ(int t)
{
    t=sons[t][1];
    while(sons[t][0]) t=sons[t][0];
    splay(t,0);
    return t;
}

void del(int x) //del(number)
{
    splay(x,0);

    int y=sons[x][0];
    while(!sons[y][1])
    {
        y=sons[y][1];
    }

    int z=sons[x][1];
    while(!sons[z][0])
    {
        z=sons[z][0];
    }

    if (y+z==0)
    {
        spt=0;
        spttail=0;
        return ;
    }

    if (y)
    {
        splay(y,0);
        size[y]--;
    }
    if (z)
    {
        splay(z,y);
        sons[z][0]=0;
        size[z]--;
    }
}

int rank(int v) //rank(value)
{
    search(spt,v);
    return size[sons[spt][0]]+1;
}

int main()
{
    memset(father,0,sizeof(father));
    memset(size,0,sizeof(size));
    memset(sons,0,sizeof(sons));
    memset(data,0,sizeof(data));
    spt=0;
    spttail=0;

    return 0;
}
时间: 2024-12-16 11:02:06

伸展树 Splay 模板的相关文章

wikioi 1396 伸展树(模板)

题目描述 Description Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额.分析营业情况是一项相当复杂的工作.由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题.经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值 = min{|该天

# 伸展树 Splay

伸展树 Splay 维基百科上称为伸展树,但是国内好像一般叫平衡树,是众多平衡树中比较优秀的一种. 平衡树左旋右旋不会影响中序遍历顺序. 一棵平衡树的中序遍历顺序是值递增排序的,相当于从小到大到大排了一次序. 平衡树的作用: 平衡树其实就是一棵二叉搜索树,set和map都是平衡树实现. 一棵二叉搜索树理论深度是\(O(log(n))\),但是当二叉树退化成链表的时候,深度就变成了\(O(n)\),很多\(O(log)\)级别从操作会退化成\(O(n)\)线性级别的操作.平衡树就是在不改变二叉搜索

树-伸展树(Splay Tree)

伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二叉查找树,即它具有和二叉查找树一样的性质:假设x为树中的任意一个结点,x节点包含关键字key,节点x的key值记为key[x].如果y是x的左子树中的一个结点,则key[y] <= key[x]:如果y是x的右子树的一个结点,则key[y] >= key[x]. (02) 除了拥有二叉查找树的性质

POJ 3580 - SuperMemo - [伸展树splay]

题目链接:http://poj.org/problem?id=3580 Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1, A2, ... An}. Then the h

伸展树Splay

新学的,其实吧,就那么回事.... 看了几天,splay处理序列问题,真的非常厉害,翻转,插入,删除,线段树实现不了的功能,splay用起来很方便. POJ 3580 SuperMemo 这题基本就是检验模板的题,各种操作都有,错了好多次,发现以前写的代码有错了的,数据水点,给水过了,注意pushup. Splay模板 #include <cstdio> #include <cstring> #include <map> #include <algorithm&g

伸展树(splay tree)

伸展树同样是一种平衡二叉搜索树,它的优势在于,在足够长的序列中能保证分摊意义上的高效率,同时也无需记录高度或者平衡因子等信息. 伸展树的高效前提是局部性:刚刚被访问到的数据,可能在短时间内被再次访问:将被访问的下一元素,可能就在不久之前刚刚访问过的元素的附近.因此,伸展树的策略,就是把刚刚访问到的节点,及时"伸展"到树根附近. 所谓"伸展"操作,其实就是BST中的旋转操作.如果每次经过适当旋转,将访问的节点提升一层,直到上升到树根,称为逐层伸展.可以验证,这种伸展方

【BBST 之伸展树 (Splay Tree)】

最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codeforces.com/problemset/problem/675/D,运行效果居然还挺好的,时间快了大概10%,内存少了大概30%. 1 #include <cstdio> 2 #include <cstring> 3 #include <string> 4 #include

浅谈伸展树(Splay)

//本文是一个暂时的小记,有不对的请大佬们指出~ 真正大佬的在这http://blog.csdn.net/clove_unique/article/details/50630280 伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由丹尼尔·斯立特Daniel Sleator和罗伯特·恩卓·塔扬Robert Endre Tarjan在1985年发明的. 在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作

模板——伸展树 splay 实现快速分裂合并的序列

伸展操作:将treap中特定的结点旋转到根 //将序列中从左数第k个元素伸展到根,注意结点键值保存的是原序列id void splay(Node* &o, int k) { int s = o->ch[0] == NULL ? 0 : o->ch[0]->s; int d = k <= s ? 0 : (k == s+1 ? -1 : 1); if(d == 1) k -= s+1; if(d != -1) { splay(o->ch[d], k); rotate(o