【数据结构】【范浩强treap】

treap其实就是一颗二叉搜索树和堆性质的一颗二叉树。而我要讲的这个是不旋转的treap,它有key值和fix值两个值,我们要满足二叉搜索树的性质以中序遍历来按照key值排序,fix值是为了让树的高度满足期望,一个点的fix值比他的儿子都小(或者大),因此fix值应用一个随机函数刷。不旋转treap支持以下操作。

    1.Merge【合并】【O(logn)】
    2.Split【拆分】【O(logn)】
    3.Newnode【新建节点】【O(1)】

可支持操作:

    1.Insert【Newnode+Merge】【O(logn)】
    2.Delete【Split+Split+Merge】【O(logn)】
    3.Find_kth【Split+Split】【O(logn)】
    4.Query【Split+Split】【O(logn)】
    5.Cover【Split+Split+Merge】【O(logn)】

那么我先说Merge,我们首先要保证fix,那么接下来我们要保证key值我们保证要合并的俩棵树前面的一颗树比后面的树的所有点的key值都小,那么如果后面的树的当前节点的fix更小,我们就把前面的树的当前节点放在左儿子,如果大就反过来。下面是代码。

int Merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(tree[x].fix<tree[y].fix)
    {
        tree[x].ch[1]=Merge(tree[x].ch[1],y);
        Update(x);
        return x;
    }
    else
    {
        tree[y].ch[0]=Merge(x,tree[y].ch[0]);
        Update(y);
        return y;
    }
}

那个Update是用来更新儿子个数的,后面要用。

void Update(int cur)
{
    tree[cur].sz=1+tree[tree[cur].ch[0]].sz+tree[tree[cur].ch[1]].sz;
}

然后是Split,我们要把一颗树按照key值分成大于key的树和小于key的两棵树,这个就直接分了吧。。。见代码。

void Split(int now,int k,int &x,int &y)
{
    if(now==0)
        x=y=0;
    else
    {
        if(tree[now].key<=k)
        {
            x=now;
            Split(tree[now].ch[1],k,tree[now].ch[1],y);
        }
        else
        {
            y=now;
            Split(tree[now].ch[0],k,x,tree[now].ch[0]);
        }
        Update(now);
    }
}

最后我们总要新建节点吧,那么就直接新建了吧。

int NewNode(int val)
{
    ncnt++;
    tree[ncnt].ch[0]=tree[ncnt].ch[1]=0;
    tree[ncnt].key=val;
    tree[ncnt].fix=rand();
    tree[ncnt].sz=1;
    return ncnt;
}

我们有了上面的操作之后就可以进行强大的操作了,比如insert我们首先把要插入的节点的val值作为key值把原来的树分开然后再新建节点,把节点合并放入较小的树再合并就像这样。

void insert(int val)
{
    int x,y,z;
    Split(root,val,x,y);
    z=NewNode(val);
    root=Merge(Merge(x,z),y);
}

猜你喜欢

转载自blog.csdn.net/qq_35713030/article/details/78709204