码迷,mamicode.com
首页 > 其他好文 > 详细

Splay模板

时间:2018-01-01 19:25:32      阅读:159      评论:0      收藏:0      [点我收藏+]

标签:角色   blog   等于   span   pre   dip   oid   i++   最大的   

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
inline int read()
{
    char c = getchar(); int x = 0, f = 1;
    while (c<0 || c>9) { if (c == -)f = -1; c = getchar(); }
    while (c >= 0&&c <= 9) { x = x * 10 + c - 0; c = getchar(); }
    return x * f;
}
int root;
struct trnode
{
    int d, n, c, f, son[2];//d为对应节点的值,n为这个值的个数,c为以他为根节点的子树的节点数,f为它的父亲,son[0]为左儿子,son[1]为右儿子
}tr[110000]; int len;//len为节点编号

void update(int x)//更新x所控制的节点数 
{
    int lc = tr[x].son[0], rc = tr[x].son[1];//左儿子与右儿子
    tr[x].c = tr[lc].c + tr[rc].c + tr[x].n;//这棵子树的节点数等于左儿子子树加上右儿子子树加上这个值的个数
}
void add(int d, int f)//添加值为d的点,认f为父亲,同时,f也认它为孩子
{
    len++;
    tr[len].d = d; tr[len].n = 1;  tr[len].c = 1;
    tr[len].f = f; if (d<tr[f].d) tr[f].son[0] = len; else tr[f].son[1] = len;
    tr[len].son[0] = tr[len].son[1] = 0;//他是叶子节点
}

void rotate(int x, int w)//左旋(x,0),或者右旋 (x,1)
{
    int f = tr[x].f, ff = tr[f].f;//x的父亲为f,x的爷爷为ff
                                  //下来建立关系 
    int r, R;//r表示儿辈,R表示父辈 
             //有四个角色:我x,我的儿子,我的父亲,我的爷爷 
    r = tr[x].son[w]; R = f;//x的儿子 ->准备当新儿子 ,x的儿子为儿辈,x的父亲为父辈
    tr[R].son[1 - w] = r;
    if (r != 0) tr[r].f = R;

    r = x; R = ff;//x ->准备当新儿子,x为儿辈,x的爷爷为父辈
    if (tr[R].son[0] == f) tr[R].son[0] = r; else  tr[R].son[1] = r;
    tr[r].f = R;

    r = f; R = x; //x的父亲 ->准备当新儿子,x的父亲为儿辈,x为父辈
    tr[R].son[w] = r;
    tr[r].f = R;

    update(f); // 先更新处于下层的点f
    update(x); // 再更新上层的x
}

void splay(int x, int rt)// 该函数功能是为了让x成为rt的孩子(左或右都行)
{
    while (tr[x].f != rt) // 如果rt是x的父亲,则什么都不用做,否则x就要不断向上旋转 
    {
        int f = tr[x].f, ff = tr[f].f; // f为x的父亲,ff为x的爷爷
        if (ff == rt) //如果 x的爷爷是rt,那么x只需要旋转一次(相当于跳一层)
        {
            if (tr[f].son[0] == x) rotate(x, 1); else rotate(x, 0);//x为其父亲的左儿子就右旋否则左旋
        }
        else
        {
            if (tr[ff].son[0] == f && tr[f].son[0] == x) { rotate(f, 1); rotate(x, 1); }//x的父亲为爷爷的左儿子,x为左儿子,f右旋,x右旋
            else if (tr[ff].son[1] == f && tr[f].son[1] == x) { rotate(f, 0); rotate(x, 0); }//x的父亲为右儿子,x为右儿子,f左旋,x左旋
            else if (tr[ff].son[0] == f && tr[f].son[1] == x) { rotate(x, 0); rotate(x, 1); }//x的父亲为左儿子,x为右儿子,x先左旋再右旋
            else if (tr[ff].son[1] == f && tr[f].son[0] == x) { rotate(x, 1); rotate(x, 0); }//x的父亲为右儿子,x为左儿子,x先右旋再左旋
        }
    }
    if (rt == 0) root = x;//x为根
}
int findip(int d)   //找值为d的节点的地址,补充:如果不存在d,有可能是接近d的(或大或小)
{
    int x = root;//根节点
    while (tr[x].d != d)//找到了就结束
    {
        if (d<tr[x].d)//小于当前节点的值,就往它的左子树找
        {
            if (tr[x].son[0] == 0) break;//没有左儿子
            else x = tr[x].son[0];
        }
        else // if( tr[x].d<d),往它的右子树找
        {
            if (tr[x].son[1] == 0) break;//没有右儿子
            else x = tr[x].son[1];
        }
    }
    return x;//返回编号
}

void ins(int d)//插入数值为d的一个节点
{
    if (root == 0) { add(d, 0); root = len; return; }//当整棵树都为空时

    int x = findip(d);//d在树中的编号
    if (tr[x].d == d)//如果找到和它值一样的点,这个值的个数加1
    {
        tr[x].n++;
        update(x);
        splay(x, 0);
    }
    else
    {
        add(d, x);
        update(x);
        splay(len, 0);//将这个点旋转到根节点
    }
}

void del(int d)   // 删除数值为d的一个节点
{
    int x = findip(d); splay(x, 0);  //找人,并且让x 当树根

    if (tr[x].n>1) { tr[x].n--; update(x); return; }   // 这个值的点有多个,就不用删点,直接个数减1

    if (tr[x].son[0] == 0 && tr[x].son[1] == 0) { root = 0; len = 0; }     // 这棵树只有它一个节点,将它删去,整个树清空
    else if (tr[x].son[0] == 0 && tr[x].son[1] != 0) { root = tr[x].son[1]; tr[root].f = 0; }//它没有左儿子,删点,将它右儿子作为根
    else if (tr[x].son[0] != 0 && tr[x].son[1] == 0) { root = tr[x].son[0]; tr[root].f = 0; }//他没有右儿子,删点,将它左儿子作为根
    else //if( tr[x].son[0]!= 0&& tr[x].son[1]!=0)左儿子右儿子都有
    {
        int p = tr[x].son[0]; while (tr[p].son[1] != 0) p = tr[p].son[1]; splay(p, x);//找到它左子树中最大的节点将他旋转到x的左儿子

        int r = tr[x].son[1], R = p;//儿辈是x得右儿子,父辈是x的左儿子

        tr[R].son[1] = r;//将x的右儿子作为x左儿子的右儿子
        tr[r].f = R;//更改它的父亲

        root = R; tr[root].f = 0;//更改根节点的信息
        update(R);
    }
}
int findpaiming(int d)//找排名 
{
    int x = findip(d); splay(x, 0);//将它旋转到根节点

    return tr[tr[x].son[0]].c + 1;//由于它的左子树的值都比他小,它的排名就为左子树节点个数加1
}

int  findshuzi(int k)  // 找排名为k的值
{
    int x = root;//根节点
    while (1)
    {
        int lc = tr[x].son[0], rc = tr[x].son[1];//左儿子,右儿子
        if (k <= tr[lc].c) x = lc; //去左孩子找
        else if (k>tr[lc].c + tr[x].n) { k -= tr[lc].c + tr[x].n;  x = rc; } //去右孩子找
        else break;  //就是你
    }
    splay(x, 0);//将找到的这个数旋转到根节点
    return tr[x].d;//返回值
}
int findqianqu(int d) //找前驱(小于d的最大数)
{
    int x = findip(d); splay(x, 0);
    if (d <= tr[x].d && tr[x].son[0] != 0)//如果是if( d<tr[x].d && tr[x].son[0]!=0 )则找到的是:小于等于d的前驱 
    {//找它左子树中最大的节点
        x = tr[x].son[0];
        while (tr[x].son[1] != 0) x = tr[x].son[1];
    }
    if (tr[x].d >= d) x = 0;//如果是if( tr[x].d >d)则找到的是:小于等于d的前驱
    return x;
}

int findhouji(int d)  //找后继(大于x的最小数)
{
    int x = findip(d); splay(x, 0);
    if (tr[x].d <= d && tr[x].son[1] != 0)
    {//找它右子树中的最小值
        x = tr[x].son[1];
        while (tr[x].son[0] != 0) x = tr[x].son[0];
    }
    if (tr[x].d <= d) x = 0;
    return x;
}
int main(){
    int n; scanf("%d", &n);
    root = 0; len = 0;
    for (int i = 1; i <= n; i++)
    {
        int cz, x; cz=read();
        x = read();
        if (cz == 1) ins(x);
        else if (cz == 2) del(x);
        else if (cz == 3) printf("%d\n", findpaiming(x));
        else if (cz == 4) printf("%d\n", findshuzi(x));
        else if (cz == 5) printf("%d\n", tr[findqianqu(x)].d);
        else if (cz == 6) printf("%d\n", tr[findhouji(x)].d);
    }
    return 0;
}

 

Splay模板

标签:角色   blog   等于   span   pre   dip   oid   i++   最大的   

原文地址:https://www.cnblogs.com/oi-forever/p/8168047.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!