• 【Splay树】


    伸展树英语:Splay Tree)是一种二叉查找树,它能在O(log n)内完成插入、查找和删除操作。它是由丹尼尔·斯立特Daniel Sleator)和罗伯特·塔扬在1985年发明的[1]

    在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。伸展树应运而生。伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。

    它的优势在于不需要记录用于平衡树的冗余信息。

                                                                   ——来自维基百科

    几个常用操作:

    其实也不一定对。。。

    rotate操作

    void rotate(int x,int &k) 
    {
        int y = f[x], z = f[y], l, r;
        if (tr[y][0] == x) l = 0; else l = 1;
        r = l ^ 1;
        if (y == k) k = x;
        else
        {
            if (tr[z][0] == y) tr[z][0] = x;
                else tr[z][1] = x;
        }
        f[x] = z; f[y] = x; f[tr[x][r]] = y;
        tr[y][l] = tr[x][r]; tr[x][r] = y;    
    }
    rotate

    把x向上旋转一个

    r = l ^ 1; 01取反

    splay操作

    void splay(int x,int &k)
    {
        int y,z;
        while (x != k)
        {
            y = f[x]; z = f[y];
            if (y != k)
            {
                if ((tr[z][0] == y)^(tr[y][0] == x)) rotate(x,k);
                    else rotate(y,k);
            }
            rotate(x,k);
        }
    }
    splay

    把x转到k的位置
    y !=k 至少要旋转两次,否则直接旋转一次x就行了
    (tr[z][0] == y)^(tr[y][0] == x) 判断在不在一条直线上
      如果是的话,先旋转父节点,再旋转自己;否则先旋转自己,再旋转父节点(就是旋转两次自己)
    因为调用了rotate,在rotate中k的值改变了,所以在这里也要加&

    插入元素

    void ins(int &k,int x,int last)
    {
        if (k == 0)
        {
            size ++; k = size; num[k] = x; f[k] = last; splay(k,rt); return;
        }
        if (x < num[k]) ins(tr[k][0],x,k);    else ins(tr[k][1],x,k);
    }
    insert

    插入一个元素
    操作完成后k的值会变成size,size会转到根,所以k的值要返回给rt,所以要加上&

    删除元素

    void del(int x)
    {
        splay(x,rt);
        if (tr[x][0] * tr[x][1] == 0)
            rt = tr[x][0] + tr[x][1];
        else
        {
            int k = tr[x][1];
            while (tr[k][0]) k = tr[k][0];
            tr[k][0] = tr[x][0]; f[tr[x][0]] = k;
            rt = tr[x][1];
        }
        f[rt] = 0;
    }
    delete

    删除编号为x的结点
    先旋转到根,如果只有一棵子树,直接删除,让子树的根作为根节点
    否则寻找右子树中最小的那个点,作为根节点

    寻找前驱

    void ask_before(int k,int x)
    {
        if (k == 0) return;
        if (num[k] <= x)
        {
            t1 = k;
            ask_before(tr[k][1],x);
        }
        else ask_before(tr[k][0],x);
    }
    ask_before

    寻找后继

    void ask_after(int k,int x)
    {
        if (k == 0) return;
        if (num[k] >= x)
        {
            t2 = k;
            ask_after(tr[k][0],x);
        }
        else ask_after(tr[k][1],x);
    }
    ask_after

    建树

    void build(int l,int r,int f)
    {
        if (l > r) return;
        int mid = (l+r) >> 1, now = id[mid], last = id[f];
        if (l == r)
        {
            size[l] = 1; tag[l] = rev[l] = 0;
            fa[l] = last;
            if (l < f) c[last][0] = now; else c[last][1] = now;
            return;
        }
        build(l,mid-1,mid); build(mid+1,r,mid);
        fa[now] = last; updata(now);
        if (mid < f) c[last][0] = now; else c[last][1] = now;
    }
    build

    id是编号,size是大小

    找第rk大的元素

    int find(int &k,int rk)
    {
        pushdown(k);
        int l = c[k][0], r = c[k][1];
        if (size[l]+1 == rk) return k;
        if (size[l] >= rk) return find(l,rk);
        return find(r,rk-size[l]-1);
    }
    找第rk大的元素

    区间+(标记)

    void add(int l,int r,int v) 
    {
        int x = find(rt,l), y = find(rt,r+2); 
        splay(x,rt); splay(y,c[x][1]);
        int z = c[y][0];
        tag[z] += v; mx[z] += v; w[z] += v;
    }
    add

    翻转(标记)

    void rever(int l,int r)
    {
        int x = find(rt,l), y = find(rt,r+2);
        splay(x,rt); splay(y,c[x][1]);
        int z = c[y][0];
        rev[z] ^= 1;
    }
    rever

    查询

    int solvemx(int l,int r)
    {
        int x = find(rt,l), y = find(rt,r+2);
        splay(x,rt); splay(y,c[x][1]); 
        int z = c[y][0];
        return mx[z];
    }
    query

    更新

    void updata(int x)
    {
        int l = c[x][0], r = c[x][1];
        size[x] = size[l] + size[r] +1;
        mx[x] = max(mx[l],mx[r]);
        mx[x] = max(mx[x],w[x]);
    }
    updata

    下传标记

    void pushdown(int x)
    {
        int l = c[x][0], r = c[x][1];
        if (rev[x])
        {
            rev[l] ^= 1; rev[r] ^= 1;
            rev[x] = 0;
            swap(c[x][0],c[x][1]);
        }
        if (tag[x])
        {
            if (l) tag[l] += tag[x], mx[l] += tag[x], w[l] += tag[x];
            if (r) tag[r] += tag[x], mx[r] += tag[x], w[r] += tag[x];
            tag[x] = 0;
        }
    }
    pushdown
  • 相关阅读:
    iOS开发其他相关
    pch文件的创建与配置
    UI界面相关
    多人开发情况下的字符串本地化
    软件系统、硬件相关
    内存管理、单例
    Info.plist文件配置及注意事项
    UI控件相关宏定义
    字体
    3分钟实现iOS语言本地化/国际化(图文详解)
  • 原文地址:https://www.cnblogs.com/liumengyue/p/5218473.html
Copyright © 2020-2023  润新知