[转]伸展树

 学习的博客


网址:http://dongxicheng.org/structure/splay-tree/

 

http://blog.csdn.net/acm_cxlove/article/details/7815019

 

http://blog.csdn.net/leolin_/article/details/6436037 

数组版 http://code.bulix.org/w8s75i-81022

 

伸展操作:

//旋转操作 0表示左旋 1表示右旋
void Rotate(int x,int kind)
{
    int y = pre[x];
    pushdown(x);
    pushdown(y);
    ch[y][!kind] = ch[x][kind];
    pre[ch[x][kind]] =  y;
    if(pre[y])
        ch[pre[y]][ch[pre[y]][1]==y] = x;
    pre[x] = pre[y];
    ch[x][kind] = y;
    pre[y] = x;
    pushup(y);
}

//将rt结点调整到goal
void splay(int rt,int goal)
{
    pushdown(rt);
    while(pre[rt] != goal)
    {
        if(pre[pre[rt]] == goal){
            Rotate(rt,ch[pre[rt]][0]==rt);
        }
        else {
            int y = pre[rt];
            int kind = (ch[pre[y]][0]==y);
            if(ch[y][kind] == rt){
                Rotate(rt,!kind);
                Rotate(rt,kind);
            }
            else {
                Rotate(y,kind);//因为这里是要转到根结点
                Rotate(rt,kind);
            }
        }
    }
    pushup(rt);
    if(goal == 0)
        root = rt;
}

这样操作过后,又有平衡树的优点,可以进行旋转,又有线段树的优点,能够找到区间询问操作。

如果要删除[l,r]区间,只要将l-1的结点调整到根,然后找到r结点,直接将根结点的有孩子连到r+1即可。

void Get_interval(int l,int r)
{
    //Get_kth()为找到第k个结点
    splay(Get_kth(l-1),0);//将l-1的结点调整为根结点
    splay(Get_kth(r+1),root);//将r+1的结点调整为根结点的孩子
                             //这样根的右孩子的左子树就是区间[l,r]
    pushup(ch[root][1]);//更新根节点的有孩子
    pushup(root);//更新根节点
}

这个函数便可以很快找到区间[l,r]执行之后的根的右孩子的左子树便是所要区间

伸展树也可以完成线段树的一些工作,也可以设置延迟标记,向上更新,向下更新等等

你可能感兴趣的:([转]伸展树)